一切福田,不離方寸,從心而覓,感無不通。

Java对象深拷贝详解

1、Java中拷贝的概念

在Java语言中,拷贝一个对象时,有浅拷贝与深拷贝两种

浅拷贝:只拷贝源对象的地址,所以新对象与老对象共用一个地址,当该地址变化时,两个对象也会随之改变。

深拷贝:拷贝对象的所有值,即使源对象发生任何改变,拷贝的值也不会变化。

在User类的基础上,介绍两种浅拷贝案列

User类:

 

案列①:普通对象的浅拷贝

 

结果:改变user1后,user2的值也随之变化

案列②:List浅拷贝(这也是我们平时项目中,经常遇到的情况)

 

结果:对List的3种拷贝,其实都是浅拷贝,当源集合中对象发生改变时,新的List也会随之变化

2、常见的深拷贝方式

  1. 构造函数方式(new的方式)
  2. 重写clone方法
  3. Apache Commons Lang序列化
  4. Gson序列化
  5. Jackson序列化

2.1、构造函数方式

这种方式就是创建一个新的对象,然后通过源对象的get方法与新对象set方法,把源对象的值复制新对象,这里就不再演示了。

缺点:在拷贝的对象数量较少时,可以使用,但是对象数量过多时,会大大增加系统开销,开发中应避免使用。

2.2、重写clone方法

步骤:

1>需要拷贝对象的类,去实现Cloneable接口

2>重写clone方法

3>使用"对象.clone()"的方式进行拷贝

根据上面的案列,进行对应的改造:

首先是User实体类 ,如下:

 

改造案列①:

 

结果:当user1改变后,user2的值不会改变

改造案列②:List类型深拷贝

 

结果:list1中的每个对象通过clone()添加list2中,当list1中的对象改变时,list2不会改变

2.3 、Apache Commons Lang序列化

步骤:

1>导入Commons包

 

2>实体类实现Serializable接口

 

3>调用SerializationUtils工具类,实现深拷贝(注意:SerializationUtils不能直接拷贝List类型)

案列如下:

案列①:对象深拷贝

 

结果:user1的改变不会导致user2的改变,从而实现深拷贝

案列②:List类型深拷贝

(1)改造开始,我们先创建一个专门用于拷贝List<User>类型的实体类

 

(2)拷贝List类型

 

结果:

 2.4、Gson序列化

步骤:

1、导入Gson依赖

 

2>创建Gson对象,使用该对象进行深拷贝(实体类不再需要实现Serializable接口)

案例如下:只演示对象的深拷贝,LIst类型的深拷贝与之前的流程是相似的

 

重点:

结果:

2.5、Jackson序列化

该方式与Gson原理、使用方式相似,但是Jackson序列化深拷贝,要求拷贝的对象必须有无参构造函数

步骤:

1>导入Jackson依赖

 

2>创建ObjectMapper对象,进行深拷贝(用法与Gson一致)

 

重点:

结果:

3、总结

方式 优点 缺点
构造函数 1. 底层实现简单 2. 不需要引入第三方包 3. 系统开销小 4. 对拷贝类没有要求,不需要实现额外接口和方法 1. 可用性差,每次新增成员变量都需要新增新的拷贝构造函数
重载clone()方法 1. 底层实现较简单 2. 不需要引入第三方包 3. 系统开销小

追求性能的可以采用该方式

1. 可用性较差,每次新增成员变量可能需要修改clone()方法 2. 拷贝类(包括其成员变量)需要实现Cloneable接口
Apache Commons Lang序列化 1. 可用性强,新增成员变量不需要修改拷贝方法 1. 底层实现较复杂 2. 需要引入Apache Commons Lang第三方JAR包 3. 拷贝类(包括其成员变量)需要实现Serializable接口 4. 序列化与反序列化存在一定的系统开销
Gson序列化 1. 可用性强,新增成员变量不需要修改拷贝方法 2. 对拷贝类没有要求,不需要实现额外接口和方法 1. 底层实现复杂 2. 需要引入Gson第三方JAR包 3. 序列化与反序列化存在一定的系统开销
Jackson序列化 1. 可用性强,新增成员变量不需要修改拷贝方法 1. 底层实现复杂 2. 需要引入Jackson第三方JAR包 3. 拷贝类(包括其成员变量)需要实现默认的无参构造函数 4. 序列化与反序列化存在一定的系统开销

 

from:https://blog.csdn.net/weixin_42675423/article/details/128260074