聊一聊深拷贝和浅拷贝

212 阅读4分钟

我正在参加「掘金·启航计划」

介绍

深拷贝:源对象和拷贝对象相互独立,一个对象改变不会影响另一个对象。

浅拷贝:拷贝对象的引用属性依旧引用着源对象的内存地址,所以一个对象的改变会影响另一个对象。

两种拷贝方式都是想获得一个新的拷贝对象,即会在内存开辟一块新的地址。

那么两种拷贝都有哪些实现方式?

新生成的拷贝对象跟源对象有啥区别呢?

他们分别生成的两个拷贝对象又有啥区别呢?

两种方式各有不同,我们分开说。

浅拷贝

实现

我们一般通过实现Cloneable接口方式来实现

  • 创建一个类ShallowCopy实现Cloneable接口

  • 直接重写clone()方法,实际的执行逻辑还是调用父类的super.clone()

@Builder
@Data
class Student implements Serializable{
    private String name;

    private int age;
}

// 定义一个浅拷贝对象
class ShallowCopy implements Cloneable{

    private Student student;

    private int version;

    public ShallowCopy(Student student, int version) {
        this.student = student;
        this.version = version;
        System.out.println("浅拷贝原型创建成功");
    }

    public Student getStudent() {
        return student;
    }

    public void setStudent(Student student) {
        this.student = student;
    }

    public int getVersion() {
        return version;
    }

    public void setVersion(int version) {
        this.version = version;
    }

    public ShallowCopy clone() throws CloneNotSupportedException {
        System.out.println("浅拷贝原型复制成功");
        return (ShallowCopy)super.clone();
    }
}

拷贝对象跟源对象的区别

上面我们通过实现Cloneable创建了一个浅拷贝对象,那么拷贝出一个对象跟原来的浅拷贝对象又有什么区别呢?我们写个demo测试看下

ShallowCopy shallowCopy1 = new ShallowCopy(Student.builder().name("学生").age(10).build(), 1);
ShallowCopy shallowCopy2 = shallowCopy1.clone();
System.out.println("浅拷贝复制的对象和原对象是否是用一个对象?" + (shallowCopy1 == shallowCopy2));
System.out.println("浅拷贝复制对象和原对象的引用类型的属性是否指向同一个地址?" + (shallowCopy1.getStudent() == (shallowCopy2.getStudent())));
System.out.println("浅拷贝复制对象和原对象的基本类型的属性是否相等?" + (shallowCopy1.getVersion() == shallowCopy2.getVersion()));

System.out.println("===================浅拷贝 is end===============");

可以看到,我们新拷贝的对象跟源对象完全是两个不同的对象。但是拷贝对象和源对象属性中的Student对象却指向的同一个地址,也就是引用的是同一个对象。那么当Student对象发生变化时,会同时影响到拷贝对象和源对象。

image.png

深拷贝

实现

上面我们知道可以通过实现Cloneable接口来实现浅拷贝。那么,我们能不能也通过实现Cloneable来实现一个深拷贝呢?为什么不可以!

  • 首先跟浅拷贝一样,创建一个类实现Cloneable接口
  • 然后重写cloue()方法
  • 但是执行逻辑跟浅拷贝就不一样了
  • 我们需要确保所有的引用类型属性都实现了Cloneable接口
  • 然后浅拷贝一个新对象
  • 并将新的拷贝对象设置到外层对象的属性中

总结一下,通过Cloneable实现深拷贝,就是将所有引用类型的属性进行浅拷贝并组装起来。


@Builder
class Teacher implements Cloneable{
    private String name;

    private int age;

    public Teacher clone() throws CloneNotSupportedException {
        return (Teacher) super.clone();
    }
}

class DeepCopyByClone implements Cloneable{

    private Teacher teacher;

    private int version;

    public DeepCopyByClone(Teacher teacher, int version) {
        this.teacher = teacher;
        this.version = version;
        System.out.println("具体原型创建成功");
    }

    public Teacher getTeacher() {
        return teacher;
    }

    public void setTeacher(Teacher teacher) {
        this.teacher = teacher;
    }

    public int getVersion() {
        return version;
    }

    public void setVersion(int version) {
        this.version = version;
    }

    public DeepCopyByClone clone() throws CloneNotSupportedException {
        DeepCopyByClone deepCopyByClone = (DeepCopyByClone) super.clone();
        deepCopyByClone.setTeacher(this.teacher.clone());
        System.out.println("具体原型复制成功");
        return deepCopyByClone;
    }
}

拷贝对象跟源对象的区别

我们继续写个demo测试下上述代码是否实现了深拷贝

DeepCopyByClone deepCopyByClone1 = new DeepCopyByClone(Teacher.builder().name("老师").age(24).build(),2);
DeepCopyByClone deepCopyByClone2 = deepCopyByClone1.clone();
System.out.println("通过实现clone深拷贝复制的对象和原对象是否是用一个对象?" + (deepCopyByClone1 == deepCopyByClone2));
System.out.println("通过实现clone深拷贝复制对象和原对象的引用类型的属性是否指向同一个地址?" + (deepCopyByClone1.getTeacher()==(deepCopyByClone2.getTeacher())));
System.out.println("通过实现clone深拷贝复制对象和原对象的基本类型的属性是否相等?" + (deepCopyByClone1.getVersion() == deepCopyByClone2.getVersion()));

可以看到,我们新拷贝的对象跟源对象不是一个对象。并且拷贝对象和源对象引用的Teacher对象指向的也不是同一个地址,也就不是同一个对象。那么,当Teacher对象改变时,不会同时影响拷贝对象和源对象了。确实是深拷贝

image.png

总结

说了这么多,两种拷贝方式有啥异同点呢

其实上面也聊到了,我在这总结下吧。

  • 首先两种方式都会新创建出一个新对象
  • 被浅拷贝的对象和源对象的属性如果是引用类型时,引用的是同一个对象;被深拷贝的对象和源对象的属性是引用类型,指向的也是不同的对象。简而言之,浅拷贝拷贝的是引用对象的指针,而深拷贝拷贝的是引用对象本身。

画个图,让大家更好的了解。

微信图片编辑_20221014200243.jpg

文中如有不足之处,欢迎指正!一起交流,一起学习,一起成长 ^v^