经常会碰到对象复制的问题,这个问题比较基础,同时与JVM内存模型挂钩。
1. 实现Cloneable接口默认的clone方法是浅拷贝
Java Cloneable接口实际上是个空接口,没有任何方法,实际的clone()是object的方法,但是是一个protected的方法,因此需要重写这个方法,并且声明为public,不然的话protected方法在其它包中是无法访问的。
问题:既然Cloneable接口是个空接口,实际的clone()是object的方法,那在类中不实现Cloneable接口是否可以?
答案:不可以,你会得到以下异常,JVM会作检查,哦,你没实现Cloneable接口,给你的不支持clone异常
java.lang.CloneNotSupportedException: ClassA
at java.lang.Object.clone(Native Method)
at ClassA.clone(ClassA.java:62)
at TestMain.main(TestMain.java:60)
测试类ClassA,浅拷贝:
public class ClassA implements Cloneable {
public int a;
public Map<String, String > map = null;
public ClassA(int a, HashMap<String, String> map){
this.a = a;
this.map = map;
}
public Object clone() {
try {
return super.clone();
}
catch (CloneNotSupportedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return null;
}
}
以上是浅拷贝,对于原始数据类型或者String这种final类型会分配新内存,但是对于其它对象类型,是浅拷贝,只创建了一个新的引用,指的是同一堆中对象。
2. 通过实现Cloneable接口的方式实现深度拷贝的方法是重写clone方法,并对当前对象内所有引用进行拷贝
所谓通过实现Cloneable实现深度拷贝,就是个苦力活,说白了是迭代的对原对象中的所有对象属性进行重写分配内存并且赋值的方式。如下面类ClassB,对于其中的Map对象的深度赋值就是迭代赋值。
public class ClassB implements Cloneable {
public int a;
public Map<String, String > map = null;
public ClassB(int a, HashMap<String, String> map){
this.a = a;
this.map = map;
}
public Object clone() throws CloneNotSupportedException {
ClassB result = (ClassB) super.clone();
Map<String, String> m = new HashMap<String, String>();
Set<String> set = this.map.keySet();
Iterator<String> iterator = set.iterator();
while(iterator.hasNext()) {
String key = iterator.next();
String value = this.map.get(key);
m.put(key, value);
}
result.map = m;
return result;
}
}
3. 使用序列化方式可以简便实现深度拷贝
个人认为深度拷贝最有效的方式,通过将当前对象序列化,然后再反序列化,如下面类ClassC中deepClone方法:
public class ClassC implements Serializable {
private static final long serialVersionUID = -6965723414666095332L;
public int a;
public Map<String, String > map = null;
public ClassC(int a, HashMap<String, String> map){
this.a = a;
this.map = map;
}
public Object deepClone() throws IOException, ClassNotFoundException {
// 序列化
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(baos);
oos.writeObject(this);
// 反序列化
ByteArrayInputStream bis = new ByteArrayInputStream(baos.toByteArray());
ObjectInputStream ois = new ObjectInputStream(bis);
return ois.readObject();
}
}
测试类:
public class TestMain {
/**
* @param args
* @throws CloneNotSupportedException
* @throws IOException
* @throws ClassNotFoundException
*/
public static void main(String[] args) throws CloneNotSupportedException, ClassNotFoundException, IOException {
HashMap<String, String> map = new HashMap<String, String>();
ClassA obj = new ClassA(10, map);
ClassA obj2 = (ClassA) obj.clone();
obj.map.put("test", "test");
System.out.println(obj);
System.out.println(obj2);
System.out.println(obj.a);
System.out.println(obj2.a);
System.out.println(obj.map.get("test"));
System.out.println(obj2.map.get("test"));
System.out.println(obj2.map == obj.map);
System.out.println("------------------------------------");
System.out.println("------------------------------------");
map = new HashMap<String, String>();
ClassB objb = new ClassB(10, map);
objb.map.put("test", "test");
ClassB objb2 = (ClassB) objb.clone();
objb.map.put("testB", "testB");
System.out.println(objb);
System.out.println(objb2);
System.out.println(objb.a);
System.out.println(objb2.a);
System.out.println(objb.map.get("test"));
System.out.println(objb2.map.get("test"));
System.out.println(objb.map.get("testB"));
System.out.println(objb2.map.get("testB"));
System.out.println(objb2.map == objb.map);
System.out.println("------------------------------------");
System.out.println("------------------------------------");
map = new HashMap<String, String>();
ClassC objc = new ClassC(10, map);
objc.map.put("test", "test");
ClassC objc2 = (ClassC) objc.deepClone();
objc.map.put("testC", "testC");
System.out.println(objc);
System.out.println(objc2);
System.out.println(objc.a);
System.out.println(objc2.a);
System.out.println(objc.map.get("test"));
System.out.println(objc2.map.get("test"));
System.out.println(objc.map.get("testC"));
System.out.println(objc2.map.get("testC"));
System.out.println(objc2.map == objc.map);
}
}
参考文章:
https://www.cnblogs.com/NaLanZiYi-LinEr/p/9192734.html
https://blog.csdn.net/baiye_xing/article/details/71788741