java中深拷贝和浅拷贝

最近遇到一个问题就是深拷贝和浅拷贝的区别,通过自己的学习,将自己的掌握的总结一下分享给大家。
深拷贝和浅拷贝其实就是对象的复制,对象的拷贝主要有一下三种形式:
1、传递引用
2、通过clone()方法浅拷贝
3、深拷贝
什么是引用传递,其实就是对象之间的赋值,将一个对象的引用赋值给另外一个对象,这样两个对象指向了同一个堆空间内的对象,通过引用对象随意改变对象中的值,那么另外一个引用也会受到影响,用一张图来说明(自己画的有点丑)
在这里插入图片描述
从示意图中可以看出来,如果obj1修改真正对象中的值,那么显然下次调用obj2时肯定也是修改以后的值。这就是引用传递
浅拷贝:真正的将对象拷贝,下面示意图
在这里插入图片描述
我们通过obj1.clone()方法实现对象的拷贝,这样的话修改值不会相互影响
但是有一个问题,在真正对象中如果有引用类型,那么使用的是引用。通过实体图说明:
在这里插入图片描述
这时候我们修改基本数据类型,如图中的1是不会影响的,但是如果对象中引用类型,那么修改以后还是会互相影响,所以就有下面的概念。
深拷贝:就是拷贝对象,而且拷贝对象中的引用
如图:
在这里插入图片描述
所以有了示意图,代码实现就不简单多了,下面贴上代码:

package com.yxc.clone;


/**
 * 英雄的介绍类
 */
public class IntroHero {
    private String word;
    public IntroHero(String word){
        this.word=word;
    }
    public String getWord() {
        return word;
    }

    public void setWord(String word) {
        this.word = word;
    }
}

package com.yxc.clone;

//如果该类要被克隆就必须实现Cloneable接口
public class Hero implements Cloneable{
   @Override
    protected Object clone() throws CloneNotSupportedException {
        return super.clone();
    }

    private int id;
    private String name;
    private IntroHero intro;

    public IntroHero getIntro() {
        return intro;
    }

    public void setIntro(IntroHero intro) {
        this.intro = intro;
    }

    public Hero(int id, String name){
        this.id=id;
        this.name=name;
    }
    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}
package com.yxc.clone;
public class CloneDemo {
    public static void main(String[] args) throws CloneNotSupportedException {
         test1();  //测试引用传递
         test2();  //测试浅拷贝
         test3();  //测试深拷贝
    }

    /**第一种简单的引用传递*/
    public static void test1(){
        //引用的复制,hero和hero1引用都指向同一个对象
        Hero hero=new Hero(1,"鲁班");
        Hero hero1=hero;
        hero.setIntro(new IntroHero("是一个射手"));
        System.out.println(hero==hero1);                       //返回true
        System.out.println(hero.getIntro()==hero1.getIntro()); //返回true
        //修改一个对象的值,观察另一个会改变嘛
        hero.setId(2);
        System.out.println(hero.getId());
        System.out.println(hero1.getId());

        /**
         * 通过这种方法拷贝的对象其实就是将引用传递过去,两者都指向同一个真正的对象
         * 所以不管通过哪一个对象修改值,都会影响另一个对象的内容
         */
    }
    /**浅拷贝*/
    public static void test2(){
        Hero hero=new Hero(1,"鲁班");
        IntroHero introHero=new IntroHero("是一个射手");
        hero.setIntro(introHero);
        try {
            Hero hero2 = (Hero)hero.clone();
            System.out.println(hero==hero2);                          //返回false
            hero.setId(2);
            System.out.println(hero.getId());   //2
            System.out.println(hero2.getId());  //1
            System.out.println(hero.getIntro()==hero2.getIntro());    //true
            //下面我们对引用类型的值进行修改
            introHero.setWord("攻速强");
            hero.setIntro(introHero);
            //我们发现下面两者输出的都是攻速强
            //这是因为在浅复制中,如果是基本数据类型的,直接将值拷贝过去,如果是引用类型,那么拷贝的对象的引用,
            //也就是说如果改变一个对象的值,那么就会影响到另一个对象的值
            System.out.println(hero.getIntro().getWord());
            System.out.println(hero2.getIntro().getWord());
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
        }

    }
    /**深拷贝
     * 有了浅拷贝的基础,那么我们只要让对象中的引用类型不一样就可以
     * */
    public static void test3(){
        Hero hero=new Hero(1,"鲁班");
        IntroHero introHero=new IntroHero("是一个射手");
        hero.setIntro(introHero);
        try {
            Hero hero3 = (Hero)hero.clone();
            System.out.println(hero==hero3);                          //返回false
            hero.setId(2);
            System.out.println(hero.getId());   //2
            System.out.println(hero3.getId());  //1
            System.out.println(hero.getIntro()==hero3.getIntro());    //true

            //这里是不一样的地方,我们制定对象不一样,这样不管修改哪一个对象,都不影响到另一个对象
            hero.setIntro(new IntroHero("攻速强"));
            System.out.println(hero.getIntro().getWord());
            System.out.println(hero3.getIntro().getWord());
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
        }

    }
}

注意要实现后面两种一定要实现接口然后重写接口中的clone方法
还有一种是真正实现深度拷贝的方法,那就是实现Serializable接口,通过对象的序列化和反序列化来实现,我们直接创建对象,然后修改值,不管是基本数据类型还是引用数据类型,都不会互相影响。
这里贴上一个主要的类,关于序列化和反序列化的

package com.yxc.clone;

import java.io.*;

public class MyUtil {
    private MyUtil() {

    }
    public static <T extends Serializable> T clone(T obj) throws Exception {
        ByteArrayOutputStream bout = new ByteArrayOutputStream();
        ObjectOutputStream oos = new ObjectOutputStream(bout);
        oos.writeObject(obj);

        ByteArrayInputStream bin = new ByteArrayInputStream(bout.toByteArray());
        ObjectInputStream ois = new ObjectInputStream(bin);
        return (T) ois.readObject();
    }
}

发布了33 篇原创文章 · 获赞 37 · 访问量 4406

猜你喜欢

转载自blog.csdn.net/weixin_42142899/article/details/101631757