1、介绍
关于Java的深拷贝和浅拷贝,简单来说就是创建一个和已知对象一模一样的对象。可能日常编码过程中用的不多,但是这是一个面试经常会问的问题,而且了解深拷贝和浅拷贝的原理,对于Java中的所谓值传递或者引用传递将会有更深的理解。
2、浅拷贝
浅拷贝就是获得拷贝对象的引用,而不是正真意义上的拷贝一个对象,例如
此时引用变量a和b 同时指向了同一个堆中的内存空间,变量b只是复制了实例A的引用地址,并不是重新在堆中开辟了一个新的空间位置,来完整的复制实例A 如图
3、深拷贝
深拷贝则是拷贝了源对象的所有值,所以即使源对象的值发生变化时,拷贝对象的值也不会改变。深拷贝则是真正意义上的拷贝,如图
4、深拷贝和浅拷贝的区别
简单来说就是一句话: 深拷贝和浅拷贝最根本的区别在于是否真正获取一个对象的复制实体,而不是引用。
5、浅拷贝的实现
首先,我们定义一下需要拷贝的简单对象。
如上述代码,我们定义了一个Student学生类,包含name姓名,和age年龄,sex性别,而是另一个School类,包含schoolName学校名称和stuNums学生数量以及Student学生,其中Student并不是字符串,而是一个Student类。接下来我们将详细描述如何签拷贝School对象。
我们看如下这段代码:
这是一个我们要进行赋值的原始类 School。下面我们产生一个 School对象,并调用其 clone 方法复制一个新的对象。
注意:调用对象的 clone 方法,必须要让类实现 Cloneable 接口,并且覆写 clone 方法。
我们查看输出的结果
s1: School [schoolName=xx大学, stuNums=2000, stu=Student [name=肉丁, age=20, sex=女]]
s1的hashcode:500977346
s1中stu1的hashcode:20132171
s2: School [schoolName=xx大学, stuNums=2000, stu=Student [name=肉丁, age=20, sex=女]]
s2的hashcode:186370029
s2中stu1的hashcode:20132171
修改克隆出来的对象
s1: School [schoolName=xx大学, stuNums=2000, stu=Student [name=斌, age=21, sex=女]]
s1的hashcode:500977346
s1中stu1的hashcode:20132171
s2: School [schoolName=xx大学, stuNums=2000, stu=Student [name=斌, age=21, sex=女]]
s2的hashcode:186370029
s2中stu1的hashcode:20132171
首先看原始类 School 实现 Cloneable 接口,并且覆写 clone 方法,它还有三个属性,一个引用类型 String定义的 schoolName,一个基本类型 int定义的 stuNums,还有一个引用类型 Student,这是一个自定义类,这个类也包含三个属性 name、age和 sex。
接着看测试内容,首先我们创建一个School类的对象s1 ,其schoolName为xx大学,stuNums为2000,学生类Stundet三个属性为 20、肉丁和女。接着我们调用 clone() 方法复制另一个对象 s2,接着打印这两个对象的内容。
从第 2 行和第 5 行打印结果:
s1的hashcode:500977346
s2的hashcode:186370029
可以看出这是两个不同的对象。
从第 1 行和第 4 行打印的对象内容看,原对象 s1 和克隆出来的对象 s2 内容完全相同。
代码中我们只是更改了克隆对象 s2 的属性Student 为斌、21、女(原对象 s1 是肉丁、20、女) ,但是从第 8 行和第 11 行打印结果来看,原对象 s1 和克隆对象 s2 的 Student属性都被修改了。
也就是说对象 School的属性 Student,经过 clone 之后,其实只是复制了其引用,他们指向的还是同一块堆内存空间,当修改其中一个对象的属性 Student,另一个也会跟着变化。
6、深拷贝的实现
深拷贝的方式有很多种,文中我们将介绍三种方式
方法一 构造函数
方法二 重载clone()方法
方法三Serializable序列化
6.1、构造函数
6.2、重载clone()方法
Object父类有个clone()的拷贝方法,不过它是protected类型的,我们需要重写它并修改为public类型。除此之外,子类还需要实现Cloneable接口来告诉JVM这个类是可以拷贝的。让我们还是看之前的School代码
我们查看输出的结果
s1: School [schoolName=xx大学, stuNums=2000, stu=Student [name=肉丁, age=20, sex=女]]
s1的hashcode:500977346
s1中stu1的hashcode:20132171
s2: School [schoolName=xx大学, stuNums=2000, stu=Student [name=肉丁, age=20, sex=女]]
s2的hashcode:186370029
s2中stu1的hashcode:2094548358
修改克隆出来的对象
s1: School [schoolName=xx大学, stuNums=2000, stu=Student [name=肉丁, age=20, sex=女]]
s1的hashcode:500977346
s1中stu1的hashcode:20132171
s2: School [schoolName=xx大学, stuNums=2000, stu=Student [name=斌, age=21, sex=女]]
s2的hashcode:186370029
s2中stu1的hashcode:2094548358
需要注意的是,super.clone()其实是浅拷贝,所以在重写School类的clone()方法时,Student对象需要调用stu.clone()重新赋值。
查看第 2 行和第 5 行
s1的hashcode:500977346
s2的hashcode:186370029
查看第 3 行和第 6 行
s1中stu1的hashcode:20132171
s2中stu1的hashcode:2094548358
通过结果发现重新复制的对象s2和s1的hashCode不同,并且s1.stu与s2.stu2的hashCode也不同,由此证明复制的新的对象和原本的对象指向的不是同一个一个对象,意味着堆内存中存在两个School实例
6.3、Serializable序列化
我们看如下的代码
注意 要使用序列化的方式来复制对象 对象需要继承Serializable接口,接下来我们查看测试类
结果如下:
277630005,1915503092
通过比较user对象和克隆的user2对象的hashCode发现,也是不同的对象
到此这篇关于Java的深拷贝与浅拷贝的几种实现方式的文章就介绍到这了,更多相关Java 深拷贝与浅拷贝内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!