Java对象克隆——浅克隆和深克隆的区别
在Java中对象的克隆有深克隆和浅克隆之分。有这种区分的原因是Java中分为基本数据类型和引用数据类型,对于不同的数据类型在内存中的存储的区域是不同的。基本数据类型存储在栈中,引用数据类型存储在堆中。
什么是克隆
克隆就是依据已经有的数据,创造一份新的完全一样的数据拷贝。
实现克隆有多种方式,可以手工的new出一个新的对象,然后将原来的对象信息一个一个的set到新的对象中。还有就是使用clone方法。使用clone方法必须满足:
实现Cloneable接口
使用public访问修饰符重新定义clone方法。
在不使用克隆方法时,将类A的实例A1直接赋给类A的新实例A2时会出现这样的情况,修改A2的属性,A1的属性也发生了改变。这是因为赋值操作之后,A1和A2指向同一个对象,就像是使用不同的显示器操作同一个服务器一样,两个显示器显示的都是一个服务器上的内容。
浅克隆
对于一个只含有基本数据类型的类来说使用clone方法,是完全没有问题的,用图表来表示该情形的clone操作:
Customer customer2=customer1.clone(); |
||
customer1 |
ID |
123 |
age |
23 |
|
customer2 |
ID |
123 |
age |
23 |
|
customer2.setAge(32); |
||
customer1 |
ID |
123 |
age |
23 |
|
customer2 |
ID |
123 |
age |
32 |
在clone后customer1和customer2之间数据互不影响。
但是如果在Customer类中有一个引用类型的属性Address呢?
- public static void main(String[] args) throws CloneNotSupportedException {
- Address address = new Address("CH" , "SD" , "QD");
- Customer customer1 = new Customer(1 , 23 , address);
- Customer customer2 = customer1.clone();
- customer2.getAddress().setCity("JN");
- customer2.setID(2);
- System.out.println("customer1:"+customer1.toString());
- System.out.println("customer2:"+customer2.toString());
- }
- }
- class Customer implements Cloneable{
- public int ID;
- public int age;
- public Address address;
- public int getID() {
- return ID;
- }
- public void setID(int iD) {
- ID = iD;
- }
- public int getAge() {
- return age;
- }
- public void setAge(int age) {
- this.age = age;
- }
- public Address getAddress() {
- return address;
- }
- public void setAddress(Address address) {
- this.address = address;
- }
- public Customer(int iD, int age, Address address) {
- super();
- ID = iD;
- this.age = age;
- this.address = address;
- }
- @Override
- public String toString() {
- return "Customer [ID=" + ID + ", age=" + age + ", address=" + address
- + "]";
- }
- @Override
- public Customer clone() throws CloneNotSupportedException {
- return (Customer) super.clone();
- }
- }
- class Address{
- private String country;
- private String province;
- private String city;
- public String getCountry() {
- return country;
- }
- public void setCountry(String country) {
- this.country = country;
- }
- public String getProvince() {
- return province;
- }
- public void setProvince(String province) {
- this.province = province;
- }
- public String getCity() {
- return city;
- }
- public void setCity(String city) {
- this.city = city;
- }
- @Override
- public String toString() {
- return "Address [country=" + country + ", province=" + province
- + ", city=" + city + "]";
- }
- public Address(String country, String province, String city) {
- super();
- this.country = country;
- this.province = province;
- this.city = city;
- }
- }
- //输出的结果是:
- //customer1:Customer [ID=1, age=23, address=Address [country=CH, province=SD, city=JN]]
- //customer2:Customer [ID=2, age=23, address=Address [country=CH, province=SD, city=JN]]
上面分析得到,clone后新旧对象互不影响,customer2修改了id后没有影响到customer1,但是修改了customer2的address属性的city值为JN后,发现customer1的address值也发生了改变。这样就没有达到完全复制、相互之间完全没有影响的目的。这样就需要进行深克隆。
2.深克隆
深克隆与浅克隆的区别就是,浅克隆不会克隆原对象中的引用类型,仅仅拷贝了引用类型的指向。深克隆则拷贝了所有。也就是说深克隆能够做到原对象和新对象之间完全没有影响。
而深克隆的实现就是在引用类型所在的类实现Cloneable接口,并使用public访问修饰符重写clone方法。
上面的代码做以下修改:
1.Address类实现Cloneable接口,重写clone方法;
- @Override
- public Address clone() throws CloneNotSupportedException {
- return (Address) super.clone();
- }
2.在Customer类的clone方法中调用Address类的clone方法。
- @Override
- public Customer clone() throws CloneNotSupportedException {
- Customer customer = (Customer) super.clone();
- customer.address = address.clone();
- return customer;
- }
修改后测试代码的输出结果:
customer1:Customer[ID=1, age=23, address=Address [country=CH, province=SD, city=QD]]
customer2:Customer[ID=2, age=23, address=Address [country=CH, province=SD, city=JN]]
发现customer2无论如何修改,customer1都没有受到影响。
实现深克隆的另一种方法就是使用序列化,将对象写入到流中,这样对象的内容就变成了字节流,也就不存在什么引用了。然后读取字节流反序列化为对象就完成了完全的复制操作了。
- Address address = new Address("CH" , "SD" , "QD");
- Customer customer1 = new Customer(1 , 23 , address);
- Customer customer2 = (Customer) cloneObject(customer1);
- customer2.getAddress().setCity("JN");
- customer2.setID(2);
- System.out.println("customer1:"+customer1.toString());
- System.out.println("customer2:"+customer2.toString());
- //customer1:Customer [ID=1, age=23, address=Address [country=CH, province=SD, city=QD]]
- //customer2:Customer [ID=2, age=23, address=Address [country=CH, province=SD, city=JN]]
cloneObject方法的定义:
- public static Object cloneObject(Object obj) throws IOException, ClassNotFoundException{
- ByteArrayOutputStream byteOut = new ByteArrayOutputStream();
- ObjectOutputStream out = new ObjectOutputStream(byteOut);
- out.writeObject(obj);
- ByteArrayInputStream byteIn = new ByteArrayInputStream(byteOut.toByteArray());
- ObjectInputStream in =new ObjectInputStream(byteIn);
- return in.readObject();
- }
个人的理解是Java中定义的clone没有深浅之分,都是统一的调用Object的clone方法。为什么会有深克隆的概念?是由于我们在实现的过程中刻意的嵌套了clone方法的调用。也就是说深克隆就是在需要克隆的对象类型的类中全部实现克隆方法。就像是toString方法一样,假如上面的Customer类中重写了toString方法,而Address类没有进行重写,就会出现这样的输出语句:
customer1:Customer[ID=1, age=23, address=com.gos.java.standard.Address@38d8fb2b]
只有在Address类也重写了toString方法才会打印出完全的信息:
customer1:Customer [ID=1, age=23, address=Address [country=CH, province=SD, city=QD]]
只不过在打印的操作中就默认的调用了对象的toString方法,而clone方法需要在代码中显式的调用。
总结:
1.浅克隆:只复制基本类型的数据,引用类型的数据只复制了引用的地址,引用的对象并没有复制,在新的对象中修改引用类型的数据会影响原对象中的引用。
2.深克隆:是在引用类型的类中也实现了clone,是clone的嵌套,复制后的对象与原对象之间完全不会影响。
3.使用序列化也能完成深复制的功能:对象序列化后写入流中,此时也就不存在引用什么的概念了,再从流中读取,生成新的对象,新对象和原对象之间也是完全互不影响的。
4.使用clone实现的深克隆其实是浅克隆中嵌套了浅克隆,与toString方法类似
Java对象克隆——浅克隆和深克隆的区别相关推荐
- java对象的浅克隆和深克隆
引言: 在Object基类中,有一个方法叫clone,产生一个前期对象的克隆,克隆对象是原对象的拷贝,由于引用类型的存在,有深克隆和浅克隆之分,若克隆对象中存在引用类型的属性,深克隆会将此属性完全拷贝 ...
- Java - 对象克隆
目录 对象克隆 浅克隆 深克隆 多层克隆 对象克隆 在讨论对象克隆之前, 可能会有人说 , 不能直接new一个吗?为什么要克隆 首先关于这个问题, 直接new一个对象, 这个对象里面包含的成员变量是n ...
- Java中的浅克隆与深克隆
Java中的浅克隆与深克隆 一:前言 二:浅克隆与深克隆的区别 一:前言 克隆,即复制一个对象,该对象的属性与被复制的对象一致,如果不使用Object类中的clone方法实现克隆,可以自己new出一个 ...
- 浅克隆与深克隆的区别及特点
浅克隆与深克隆的区别 1.浅克隆:对当前对象进行克隆,并克隆该对象所包含的8种基本数据类型和String类型属性(拷贝一份该对象并重新分配内存,即产生了新的对象):但如果被克隆的对象中包含除8中数据类 ...
- java对象克隆的例子_Java对象克隆
本篇文章帮大家学习java对象克隆,包含了Java对象克隆使用方法.操作技巧.实例演示和注意事项,有一定的学习价值,大家可以用来参考. Java不提供克隆(复制)对象的自动机制.克隆对象意味着逐位复制 ...
- Java对象克隆方法(浅克隆、深克隆)
要让一个对象进行克隆,其实就是两个步骤: 1. 让该类实现java.lang.Cloneable接口: 2. 重写(override)Object类的clone()方法. 上面的克隆方法其实是浅克隆, ...
- java 浅克隆_(Java)浅克隆与深克隆的区别
克隆,就是复制一个对象的副本,而克隆又分浅克隆和深克隆.浅克隆是指克隆得到的对象基本类型的值改变了,而源对象的值不会变.但如果被克隆对象引用类型的值改变了,那么源对象的值同样会改变,因为引用类型在栈内 ...
- 对象的浅克隆与深克隆
2019独角兽企业重金招聘Python工程师标准>>> 首先我们要知道为什么要进行对象克隆,我认为最大好处就是提高了效率,你想想用一个对象需要new一次的效率是非常低的. 这是基本的 ...
- java对象克隆效率_fastclone
fastclone 一款轻量级Java对象高效克隆框架,提供高性能的深克隆(非Object->序列化->Object这种低效率克隆).浅克隆,支持递归克隆.性能上秒杀Apache Comm ...
最新文章
- nginx 部署antd_design_pro
- 使用服务器测量网站性能,使用服务器时序测量网站性能
- java 查询表 并返回数据_ajax与java前后台传值及数据表查询解决一个bug的问题
- CodeForces - 1523E Crypto Lights(组合数学+推公式)
- 计算机专业的三行情书,各专业三行情书,看懂你就是全能学霸!
- 操作系统hpf算法事例_操作系统中常见算法汇总
- Android Activity界面切换添加动画特效 (转载修改)
- 仿照扫描全能王python程序实现
- c++使用POP协议接收以及解析邮件
- ewb交通灯报告和文件_简易交通灯控制逻辑电路设计报告
- python 常数赋值给tensor、常数和tensor比较大小、常数和tensor比较大小后作为tensor索引
- matlab 曲线数据输出,Mathlab 如何输出曲线各数据点值?
- 11 | 二进制编码:“手持两把锟斤拷,口中疾呼烫烫烫”?
- 一些开源的项目 收藏
- Android——App内文件分享功能
- 将数组转换为JSON数据
- C语言之strcmp函数和strncmp函数
- Unity Shader入门学习(5):基础屏幕后处理
- OpenGL(四) 左右手坐标系及基本坐标变换
- AD9361官方FPGA工程编译
热门文章
- 高德地图创建的地图转pdf后是空白的或者失败
- chrony配置服务器时间同步
- matlab并行fft实现,matlab fft实现相关
- FFmpeg4入门14:Linux下摄像头捕获并编码为h264
- android lib 界面库,全开源C++ DirectUI 界面库SOUI 3.0更新
- linux下ganglia监控系统搭建,开源监控软件Ganglia 安装使用
- Centos7.2 ganglia(一)之环境搭建部署
- 教育src漏洞挖掘-2022-白帽必挖出教程
- canvas图片旋转,图片base64编码,保存图片
- ResNet(残差网络模型)原理解读-包括residual block与BN原理介绍