1. 创建对象的方式
Java中使用一个对象之前,需要先实例化这个对象。
实例化对象的方式有如下几种:
- 通过new 构造方法()的方式,调用指定构造器去创建一个对象。
- 通过反射目标类的指定构造器或者默认构造器去创建一个对象。
- 通过Object的clone()方法去创建一个目标对象的副本对象。
- 通过反序列化的方式,将一个序列化后的字符串文本反序列化为一个对象。
2. 什么是clone?
2.1 为什么需要克隆?
clone:对象之间的克隆,是个老生常谈的话题。
有时候我们需要一个对象的副本去更改或者进行某些更改的操作,但是又不能改变真正的原始对象,所以就需要根据原始对象克隆一个副本对象出来。
通过对象可变和不可变知识可知,很明显引用传递改变了对象的内容啊,引用传递指向的是同一个对象实体。
因此,其实对8大基本类型和包装类型、String而言,是不需要进行对象克隆的,因为他们内容不可变,本身产生的就是一个新的对象。
2.2 对象克隆和引用传递的区别
注意,对象的引用传递不叫克隆技术,即便是浅克隆也是当前对象本身已经完成了内容的克隆,即堆内存中的内容的克隆,只不过内部引用的其他对象实体克隆的还是地址。
但是引用传递压根连当前对象本身(堆内存中的内容)都没有克隆,所以引用传递就是引用传递,指同一块堆内存空间同时被多个引用变量所指向。
可以这样理解,引用传递仅仅是对引用地址的克隆,但是这样的克隆是没有意义的。
2.3 如何实现克隆?
最笨的方法就是先new一个a对象的同类对象b,然后挨个拷贝a的属性给b中的相对应属性。
这种方式不论是深克隆还是浅克隆,对于原始对象成员变量比较多的情况,这种方式是很操蛋的,很麻烦!
所以一般使用Object类中提供的clone()方法。
2.4 clone方法
clone()方法是Object类中的方法。定义如下:
protected native Object clone() throws CloneNotSupportedException;
- clone()方法是一个native方法,native方法的效率一般来说远远高于非native方法,因为离操作系统最近;
- 它是一个protected权限的方法,所以必须覆盖此方法以扩大权限(但是要克隆需要super Object中的clone());
- 它返回一个Object,必要时需要转型;
- clone()方法提供了浅克隆的功能,意味着只能克隆原始对象的实例,而对于原始对象实例中依赖的其他对象只是克隆了引用地址,而没有克隆其对象本身。
- 要使用clone()方法完成克隆的话,其对象所对应的类必须实现Cloneable接口,该接口什么方法都没有,只是一个标识接口,表示该类的对象可以被克隆。
2.5 深克隆和浅克隆(深拷贝和浅拷贝)
浅克隆:源对象A,目标对象B只是克隆了源对象A中的第一层属性。如果源对象A中引用了另外一个引用对象X,那么目标对象B此时克隆的仅仅是X的引用地址,而不是X中保存的内容。
深克隆:和浅克隆相对,深克隆时,原对象A不论结构多么复杂,目标对象B克隆的都是源对象A中的具体内容。
2.6 浅克隆的缺点
Object的clone()方法是浅克隆。
浅克隆的方式只是将源对象的内容克隆给了目标对象,如果源对象中的成员变量都是简单类型(8大基本类型和String)的话,那么浅克隆是没有任何问题的;
但是,一旦源对象中引用了其他对象的话,那么这个对象在浅克隆时只是将该对象的引用地址克隆给了目标对象;
这样一来,如果在目标对象完成了对源对象的浅克隆动作后,源对象更改了其中的引用属性的内容,目标对象中克隆后的引用属性内容也会一并更改。
这种需要特别注意。
2.7 浅克隆案例
需要引用的第三方对象:
package zeh.myjavase.code18classlib.clone.demo01;
// 原始对象中引用的对象所在类
public class BaseInfo {
private String info;
public BaseInfo() {
}
public BaseInfo(String info) {
this.setInfo(info);
}
public String getInfo() {
return info;
}
public void setInfo(String info) {
this.info = info;
}
public String toString() {
return this.info;
}
}
源对象和目标对象描述类:
package zeh.myjavase.code18classlib.clone.demo01;
public class PersonClone implements Cloneable {
// 自定义的PersonClone类实现Cloneable接口,表示该类的实例对象可以被clone
private String name;
private int age;
private BaseInfo info;
public PersonClone() {
}
public PersonClone(String name, int age, BaseInfo info) {
this.name = name;
this.age = age;
this.info = info;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public BaseInfo getInfo() {
return info;
}
public void setInfo(BaseInfo info) {
this.info = info;
}
// 覆盖父类的clone()方法,扩大访问权限为public,以让外部访问
// 此处借助Object类的clone()方法自动完成浅克隆方式
@Override
public Object clone() throws CloneNotSupportedException {
// 当前子类如果覆盖了父类的同名方法,则默认调用的是当前子类的,如果想显式调用直接父类的方法,必须使用super。
// 具体的克隆操作交给父类完成。
return super.clone();
}
@Override
public String toString() {
return "姓名:" + this.name + ";年龄:" + this.age + ";info:" + this.info;
}
}
操作类:
package zeh.myjavase.code18classlib.clone.demo01;
import org.junit.Test;
public class Demo01StudyClone {
@Test
public void testQianClone() throws Exception {
BaseInfo info = new BaseInfo("原始info对象");
PersonClone sourcePer = new PersonClone("张三", 22, info);
PersonClone clonePer = (PersonClone) sourcePer.clone();
//说明clone后的两个对象是完全不同的对象,指向的是两块独立的堆内存空间(这不废话了,就是要堆内存空间相互独立,所以才需要克隆)
//即clone后的对象内容完全相同,但是是两个完全独立的对象。
System.out.println("sourcePer == clonePer?" + (sourcePer == clonePer));
System.out.println("原始对象:" + sourcePer);
System.out.println("克隆后对象:" + clonePer);
System.out.println("--------------------------------------");
//源对象和克隆对象中的引用类型地址是相等的,说明clone的是浅克隆,即对于引用类型克隆的是地址。
BaseInfo sourceInfo = sourcePer.getInfo();
BaseInfo cloneInfo = clonePer.getInfo();
System.out.println("sourceInfo == cloneInfo?" + (sourceInfo == cloneInfo));
//更改克隆对象的内容,对于简单类型的更改不会影响源对象。
//对于引用类型的更改,因为是同一个引用地址,所以会同时更改源对象中对应的引用类型的内容。
clonePer.setName("克隆后");
clonePer.setAge(100);
clonePer.getInfo().setInfo("修改后的info对象");// 浅克隆导致per2可以更改per1中的Info对象的值
System.out.println("浅克隆将原始对象中引用的对象的内容都给改变了...");
System.out.println("原始对象:" + sourcePer);
System.out.println("克隆后对象:" + clonePer);
}
}
运行结果:
sourcePer == clonePer?false
原始对象:姓名:张三;年龄:22;info:原始info对象
克隆后对象:姓名:张三;年龄:22;info:原始info对象
--------------------------------------
sourceInfo == cloneInfo?true
浅克隆将原始对象中引用的对象的内容都给改变了...
原始对象:姓名:张三;年龄:22;info:修改后的info对象
克隆后对象:姓名:克隆后;年龄:100;info:修改后的info对象
3. 深克隆
3.1 回顾深克隆
深克隆:
如果原始对象中引用了其他对象,则同样将其他对象的内容给复制过来,而不是仅仅克隆的是引用对象的一个引用地址。
最终克隆链实际上追踪到底部都是对8大基本类型和String等简单类型的克隆。
3.2 使用Object的clone()方法完成深克隆
实现原理:
整个对象引用链上的引用对象都需要实现Cloneable接口且覆盖clone()方法。
案例:
需要引用的第三方对象:
package zeh.myjavase.code18classlib.clone.demo02;
// 原始对象中引用的对象,深克隆中该类也必须实现Cloneable接口且覆盖clone()方法
public class BaseInfo implements Cloneable {
private String info;
public BaseInfo() {
}
public BaseInfo(String info) {
this.setInfo(info);
}
public String getInfo() {
return info;
}
public void setInfo(String info) {
this.info = info;
}
@Override
public String toString() {
return this.info;
}
// 覆盖父类的clone()方法,扩大访问权限为public,以让外部访问
@Override
public Object clone() throws CloneNotSupportedException {
// 具体的克隆操作交给父类完成
return super.clone();
}
}
源对象和目标对象描述类:
package zeh.myjavase.code18classlib.clone.demo02;
import java.util.Arrays;
public class PersonClone implements Cloneable {
// 源对象所在的类实现Cloneable接口,表示源对象可以被clone
private String name;
private int age;
// 深克隆,则对象的整个引用链引用的对象都必须实现Cloneable,且覆盖clone()方法
private BaseInfo info;
//由于数组或者其他jdk内置的引用类,我们无法进行覆盖,所以导致无法对这些引用类进行深克隆
private Integer[] intArray;
public PersonClone() {
}
public PersonClone(String name, int age, BaseInfo info) {
this.name = name;
this.age = age;
this.info = info;
}
public PersonClone(String name, int age, BaseInfo info, Integer[] intArray) {
this(name, age, info);
this.setIntArray(intArray);
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public BaseInfo getInfo() {
return info;
}
public void setInfo(BaseInfo info) {
this.info = info;
}
public Integer[] getIntArray() {
return intArray;
}
public void setIntArray(Integer[] intArray) {
this.intArray = intArray;
}
// 覆盖父类的clone()方法,扩大访问权限为public,以让外部访问。
// 此处借助Object类的clone()方法自动完成深克隆方式。
// 深克隆必须在覆盖clone()方法时,将其引用的对象的克隆方法也调用起来并设置进去。
@Override
public Object clone() throws CloneNotSupportedException {
PersonClone per = (PersonClone) super.clone();
// 调用其引用对象的clone方法完成对引用对象的内容克隆
BaseInfo in = (BaseInfo) per.getInfo().clone();
per.setInfo(in);
return per;
}
@Override
public String toString() {
return "姓名:" + this.name + ";年龄:" + this.age + ";info:" + this.info + ";intArray:" + Arrays.toString(intArray);
}
}
操作类:
package zeh.myjavase.code18classlib.clone.demo02;
public class Demo02StudyClone {
public static void main(String args[]) throws Exception {
BaseInfo info = new BaseInfo("原始Info2对象");
//源对象
PersonClone sourcePer = new PersonClone("李四", 45, info, new Integer[]{1, 2, 3});
//克隆对象
PersonClone clonePer = (PersonClone) sourcePer.clone();
System.out.println("sourcePer == clonePer?" + (sourcePer == clonePer));
System.out.println("原始对象:" + sourcePer);
System.out.println("克隆后对象:" + clonePer);
System.out.println("per1.getInfo() == per2.getInfo()?,说明clone的是深克隆:" + (sourcePer.getInfo() == clonePer.getInfo()));
System.out.println("=================================================================================");
clonePer.setName("克隆后");
clonePer.setAge(100);
// 浅克隆导致 clonePer 可以更改 sourcePer 中的Info对象的值,而深克隆不会。
clonePer.getInfo().setInfo("修改后的Info2对象");
//数组无法通过clone实现深克隆,只能使用序列化的方式
Integer[] temp = clonePer.getIntArray();
for (int i = 0; i < temp.length; i++) {
temp[i] = 234;
}
System.out.println("深克隆后,克隆对象将无法改变原始对象中引用的对象...");
System.out.println("原始对象:" + sourcePer);
System.out.println("克隆后对象:" + clonePer);
}
}
运行结果:
sourcePer == clonePer?false
原始对象:姓名:李四;年龄:45;info:原始Info2对象;intArray:[1, 2, 3]
克隆后对象:姓名:李四;年龄:45;info:原始Info2对象;intArray:[1, 2, 3]
per1.getInfo() == per2.getInfo()?,说明clone的是深克隆:false
=================================================================================
深克隆后,克隆对象将无法改变原始对象中引用的对象...
原始对象:姓名:李四;年龄:45;info:原始Info2对象;intArray:[234, 234, 234]
克隆后对象:姓名:克隆后;年龄:100;info:修改后的Info2对象;intArray:[234, 234, 234]
3.3 使用序列化方式实现深克隆
原理:
使用序列化方式实现深克隆技术,将源对象序列化到内存流,然后反序列化一个副本对象出来。
序列化操作实现深克隆,则整个引用链的对象都必须实现Serializable接口,这样才能保证将所有的成员变量都进行了序列化。
案例:
需要引用的第三方对象:
package zeh.myjavase.code18classlib.clone.demo03;
import java.io.Serializable;
// 原始对象中引用的对象,该引用的对象也必须实现Serializable接口
public class BaseInfo implements Serializable {
private static final long serialVersionUID = 2217979793879861308L;
private String info;
public BaseInfo() {
}
public BaseInfo(String info) {
this.setInfo(info);
}
public String getInfo() {
return info;
}
public void setInfo(String info) {
this.info = info;
}
public String toString() {
return this.info;
}
}
源对象和目标对象描述类:
package zeh.myjavase.code18classlib.clone.demo03;
import java.io.*;
import java.util.Arrays;
public class PersonClone3 implements Serializable {
private static final long serialVersionUID = -6747126867152704872L;
private String name;
private int age;
// 序列化方式实现深克隆,则整个引用链上的po对象所在的类都必须实现Serializable,以表示整个引用链的对象都可序列化。
private BaseInfo info;
//引用数组
private int[] intArray;
public PersonClone3() {
}
public PersonClone3(String name, int age, BaseInfo info) {
this.name = name;
this.age = age;
this.info = info;
}
public PersonClone3(String name, int age, BaseInfo info, int[] intArray) {
this(name, age, info);
this.setIntArray(intArray);
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public BaseInfo getInfo() {
return info;
}
public void setInfo(BaseInfo info) {
this.info = info;
}
public int[] getIntArray() {
return intArray;
}
public void setIntArray(int[] intArray) {
this.intArray = intArray;
}
@Override
public String toString() {
return "姓名:" + this.name + ";年龄:" + this.age + ";info:" + this.info + ";intArray:" + Arrays.toString(intArray);
}
// 使用序列化方式完成深克隆
public Object deepClone() throws IOException, ClassNotFoundException {
// 实例化内存输出流,将对象序列化到内存中
ByteArrayOutputStream baos = new ByteArrayOutputStream();
// 实例化对象输出流,将对象序列化到指定的内存中
ObjectOutputStream oops = new ObjectOutputStream(baos);
// 将当前对象序列化
oops.writeObject(this);
// 实例化内存输入流,共享前面的内存输出流
ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
ObjectInputStream oips = new ObjectInputStream(bais);
// 反序列化
return oips.readObject();
}
}
操作类:
package zeh.myjavase.code18classlib.clone.demo03;
import java.io.IOException;
public class Demo03StudyClone {
public static void main(String args[]) throws IOException,
ClassNotFoundException {
BaseInfo info = new BaseInfo("原始Info3对象");
PersonClone3 per1 = new PersonClone3("李四", 45, info, new int[]{1, 2, 3});
PersonClone3 per2 = (PersonClone3) per1.deepClone();
System.out.println("per1 == per2?" + (per1 == per2));
System.out.println("原始对象:" + per1);
System.out.println("克隆后对象:" + per2);
System.out.println("per1.getInfo() == per2.getInfo()?,说明clone的是深克隆:"
+ (per1.getInfo() == per2.getInfo()));
per2.setName("克隆后");
per2.setAge(100);
// 深克隆后,per2将不能更改per1中的Info对象的值,per2与per1实现克隆后即完全独立
per2.getInfo().setInfo("修改后的Info3对象");
//数组无法通过clone实现深克隆,只能使用序列化的方式
int[] temp = per2.getIntArray();
for (int i = 0; i < temp.length; i++) {
temp[i] = 234;
}
System.out.println("深克隆后,克隆对象将无法改变原始对象中引用的对象...");
System.out.println("原始对象:" + per1);
System.out.println("克隆后对象:" + per2);
}
}
运行结果:
per1 == per2?false
原始对象:姓名:李四;年龄:45;info:原始Info3对象;intArray:[1, 2, 3]
克隆后对象:姓名:李四;年龄:45;info:原始Info3对象;intArray:[1, 2, 3]
per1.getInfo() == per2.getInfo()?,说明clone的是深克隆:false
深克隆后,克隆对象将无法改变原始对象中引用的对象...
原始对象:姓名:李四;年龄:45;info:原始Info3对象;intArray:[1, 2, 3]
克隆后对象:姓名:克隆后;年龄:100;info:修改后的Info3对象;intArray:[234, 234, 234]
4. 高级:对Map集合的深克隆
HashMap类本身已经实现了Cloneable接口并且覆盖了Object类中的clone()方法了。源码如下:
@Override
public Object clone() {
HashMap<K,V> result;
try {
result = (HashMap<K,V>)super.clone();
} catch (CloneNotSupportedException e) {
// this shouldn't happen, since we are Cloneable
throw new InternalError(e);
}
result.reinitialize();
result.putMapEntries(this, false);
return result;
}
hashMap实现的clone方法和其他对象的一样,都只能完成浅克隆,并且不能改造成深克隆。
同时hashMap还提供了一种putAll()方法能方便的实现浅克隆。
要说map集合和list集合等的clone和普通java对象的克隆并没有什么不同。
只不过map中封装了putAll()方法能够方便的实现浅克隆。
hasnMap由于内部覆盖了clone()方法,并且我们不能去介入它的实现,因此,hashMap不能通过clone链的方式实现深克隆。
要想实现hashMap的深克隆,只能通过反序列化的方式去实现。
我们通过案例来讲解。
先准备两个java bean。
原始对象引用的bean:
package zeh.myjavase.code18classlib.clone.demo04;
// 原始对象中引用的对象所在类
public class BaseInfoNotSer {
private String info;
public BaseInfoNotSer() {
}
public BaseInfoNotSer(String info) {
this.setInfo(info);
}
public String getInfo() {
return info;
}
public void setInfo(String info) {
this.info = info;
}
public String toString() {
return this.info;
}
}
原始对象引用的bean,该bean实现了序列化接口。
package zeh.myjavase.code18classlib.clone.demo04;
import java.io.Serializable;
// 原始对象中引用的对象,该引用的对象也必须实现Serializable接口
public class BaseInfoSer implements Serializable {
private static final long serialVersionUID = 2217979793879861308L;
private String info;
public BaseInfoSer() {
}
public BaseInfoSer(String info) {
this.setInfo(info);
}
public String getInfo() {
return info;
}
public void setInfo(String info) {
this.info = info;
}
public String toString() {
return this.info;
}
}
map对象的引用传递:
//实现map对象之间的引用传递,发现克隆map完全可以更改原始map的内容,所以此处是引用传递,不属于克隆操作
@Test
public void testMapYinYongChuanDi() {
Map<String, Integer> sourceMap = new HashMap<String, Integer>();
sourceMap.put("sourceMap", 1);
System.out.println("克隆前的原始map:" + sourceMap);
//只是将 sourceMap 的引用传递给了 cloneMap,此时 cloneMap 和 sourceMap 执行同一块堆内存空间
Map<String, Integer> cloneMap = sourceMap;
//修改 cloneMap ,观察原始 sourceMap 中的内容是否发生改变
cloneMap.put("cloneMap", 100);
cloneMap.put("cloneMap2", 1000);
System.out.println("sourceMap:" + sourceMap);
System.out.println("cloneMap:" + cloneMap);
//总结:引用传递不属于克隆,接收方可以同时修改源对象中的内容。
}
运行结果:
克隆前的原始map:{sourceMap=1}
sourceMap:{cloneMap2=1000, cloneMap=100, sourceMap=1}
cloneMap:{cloneMap2=1000, cloneMap=100, sourceMap=1}
putAll的方式实现map对象的浅克隆:
// 使用putAll方法实现浅克隆 ,putAll方法只是将map中的简单数据类型进行了内容拷贝;
// 对于引用类型只进行了引用地址的拷贝而不是拷贝引用指向的内容。
@Test
public void testMapQianCloneByPutAll() {
Map<String, Object> sourceMap = new HashMap<String, Object>();
sourceMap.put("sourceMap", 1);
sourceMap.put("info", new BaseInfoNotSer("source"));
System.out.println("sourceMap:" + sourceMap);
Map<String, Object> cloneMap = new HashMap<String, Object>();
cloneMap.putAll(sourceMap);// 通过putAll()方法实现map对象之间的浅克隆操作
cloneMap.put("mapCopy2", 2);
cloneMap.put("mapSource2", 10);
//putAll对于引用类型,克隆对象可以修改源对象的内容。
((BaseInfoNotSer) cloneMap.get("info")).setInfo("clone...");
System.out.println("sourceMap:" + sourceMap);
System.out.println("cloneMap:" + cloneMap);
}
运行结果:
sourceMap:{sourceMap=1, info=source}
sourceMap:{sourceMap=1, info=clone...}
cloneMap:{sourceMap=1, mapCopy2=2, info=clone..., mapSource2=10}
clone方法的方式实现map对象的浅克隆:
// 使用clone方法实现浅克隆【HashMap类实现了Cloneable接口,覆盖了Clone()方法】
// 同putAll,hashMap实现的clone也只是浅克隆,对于map中的引用对象直接clone的是引用地址。
@Test
public void testMapQianCloneByClone() {
//必须使用子类HashMap去接收,因为只有Map的子类覆盖了clone()方法,且实现了Serializable接口,而Map接口本身没有。
HashMap<String, Object> sourceMap = new HashMap<String, Object>();
sourceMap.put("sourceMap", 1);
sourceMap.put("info", new BaseInfoNotSer("sourceMap"));
// 通过clone()方法实现
Map<String, Object> cloneMap = (Map<String, Object>) sourceMap.clone();
cloneMap.put("cloneMap", 3);
cloneMap.put("cloneMap2", 167);
System.out.println("sourceMap:" + sourceMap);
System.out.println("cloneMap:" + cloneMap);
// 源对象中取出BaseInfoNotSer对象,然后重置它的内容。
((BaseInfoNotSer) sourceMap.get("info")).setInfo("cloneMap");
// 重置后发现,源对象和目标对象的BaseInfoNotSer中的内容都繁盛了变化
System.out.println("sourceMap:" + sourceMap);
System.out.println("cloneMap:" + cloneMap);
}
运行结果:
sourceMap:{sourceMap=1, info=sourceMap}
cloneMap:{cloneMap2=167, cloneMap=3, sourceMap=1, info=sourceMap}
sourceMap:{sourceMap=1, info=cloneMap}
cloneMap:{cloneMap2=167, cloneMap=3, sourceMap=1, info=cloneMap}
通过反序列化的方式实现map对象的深克隆:
使用前,先封装一个通用的反序列化工具类:
package zeh.myjavase.code18classlib.clone.util;
import java.io.*;
// 封装一个类,专门用来实现序列化方式深克隆map【该类可以用来克隆任何java对象,不仅仅是map】
public class CloneUtil {
// 泛型化的静态方法,方便通过CloneUtil类直接调用
// 基于内存输入输出流的实现对象序列化
public static <T extends Serializable> T deepClone(T obj) {
T cloneObj = null;
// 实例化内存输出流,将对象序列化到内存中
ByteArrayOutputStream baos;
ObjectOutputStream oops;
ObjectInputStream oips;
try {
// 实例化内存输出流
baos = new ByteArrayOutputStream();
// 实例化对象输出流,将对象序列化到指定的内存中
oops = new ObjectOutputStream(baos);
// 将接收到的对象序列化到内存输出流
oops.writeObject(obj);
oops.close();
// 实例化内存输入流,共享前面的内存输出流
ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
// 实例化对象输入流
oips = new ObjectInputStream(bais);
// 反序列化方式从内存输入流中读取克隆后对象
cloneObj = (T) oips.readObject();
} catch (Exception e) {
e.printStackTrace();
}
// 返回克隆后的对象
return cloneObj;
}
}
操作类:
// 实现map对象之间的深克隆,克隆当前map对象中的所有内容,包括整个引用链引用的对象的内容(而不是克隆引用)。
// map对象之间的深克隆,只能使用序列化的方式进行,但是前提是原始map必须直接使用HashMap类进行接收,因为Map接口没有实现Serializable接口;
// 使用clone()不能够实现map的深克隆,尽管HashMap类实现了Cloneable接口,但是覆盖的clone()方法并没有实现整个引用链的深克隆。
// 对于ArrayList,也只能使用序列化的方式进行深克隆,ArrayList类也实现了Serializable接口。
// 引用的对象所在类必须实现 Serializable 标识接口。
@Test
public void testMapDeepClone() {
// 必须使用子类HashMap去接收
HashMap<String, Object> sourceMap = new HashMap<String, Object>();
sourceMap.put("sourceMap", 1);// map中放置整型
sourceMap.put("info", new BaseInfoSer("sourceMap"));// map中放置引用类型
// clone的方式尝试实现map的深克隆
Map<String, Object> cloneMap = (Map<String, Object>) sourceMap.clone();
System.out.println("sourceMap:" + sourceMap);
System.out.println("cloneMap:" + cloneMap);
System.out.println("==============================================");
// 通过源map获取引用成员然后更新其值
((BaseInfoSer) sourceMap.get("info")).setInfo("fixCode");
// 发现使用克隆对象更改sourceMap中引用的BaseInfoSer的值,结果源map和目标map的内容都同步更改了
// 所以,clone不能实现map的深克隆
System.out.println("sourceMap:" + sourceMap);
System.out.println("cloneMap:" + cloneMap);
System.out.println("==============================================");
// 序列化方式实现深克隆
cloneMap = CloneUtil.deepClone(sourceMap);
// 更改目标map中引用对象的内容,发现不影响源map,说明序列化方式完成的是深克隆
cloneMap.put("cloneMap", 5);
((BaseInfoSer) cloneMap.get("info")).setInfo("测试");
System.out.println("sourceMap:" + sourceMap);
System.out.println("cloneMap:" + cloneMap);
System.out.println("总结:序列化方式能够实现map对象之间的深克隆");
}
运行结果:
sourceMap:{sourceMap=1, info=sourceMap}
cloneMap:{sourceMap=1, info=sourceMap}
==============================================
sourceMap:{sourceMap=1, info=fixCode}
cloneMap:{sourceMap=1, info=fixCode}
==============================================
sourceMap:{sourceMap=1, info=fixCode}
cloneMap:{cloneMap=5, sourceMap=1, info=测试}
总结:序列化方式能够实现map对象之间的深克隆
文档信息
- 本文作者:Marshall