我正在参加「掘金·启航计划」
介绍
深拷贝:源对象和拷贝对象相互独立,一个对象改变不会影响另一个对象。
浅拷贝:拷贝对象的引用属性依旧引用着源对象的内存地址,所以一个对象的改变会影响另一个对象。
两种拷贝方式都是想获得一个新的拷贝对象,即会在内存开辟一块新的地址。
那么两种拷贝都有哪些实现方式?
新生成的拷贝对象跟源对象有啥区别呢?
他们分别生成的两个拷贝对象又有啥区别呢?
两种方式各有不同,我们分开说。
浅拷贝
实现
我们一般通过实现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对象发生变化时,会同时影响到拷贝对象和源对象。
深拷贝
实现
上面我们知道可以通过实现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对象改变时,不会同时影响拷贝对象和源对象了。确实是深拷贝
总结
说了这么多,两种拷贝方式有啥异同点呢
其实上面也聊到了,我在这总结下吧。
- 首先两种方式都会新创建出一个新对象
- 被浅拷贝的对象和源对象的属性如果是引用类型时,引用的是同一个对象;被深拷贝的对象和源对象的属性是引用类型,指向的也是不同的对象。简而言之,浅拷贝拷贝的是引用对象的指针,而深拷贝拷贝的是引用对象本身。
画个图,让大家更好的了解。
文中如有不足之处,欢迎指正!一起交流,一起学习,一起成长 ^v^