在Java中, 类可以通过实现 Cloneable
接口来实现.
但是由此方法拷贝的对象是浅拷贝. 具体地说, 对于对象内部的基本类型是深拷贝, 而引用类型是浅拷贝.
查看以下代码: (省略constructor, getter, setter, toString…)
package prototype;
import java.io.Serializable;
public class Bar implements Serializable {
private String name;
}
package prototype;
import java.io.*;
public class Prototype implements Cloneable, Serializable {
private long id;
private Bar bar;
@Override
public Prototype clone() {
try {
return (Prototype) super.clone();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
return null;
}
}
public Prototype deepClone() throws Exception {
var baos = new ByteArrayOutputStream();
var oos = new ObjectOutputStream(baos);
oos.writeObject(this);
var bais = new ByteArrayInputStream(baos.toByteArray());
var ois = new ObjectInputStream(bais);
return (Prototype) ois.readObject();
}
}
package prototype;
public class Client {
public static void main(String[] args) throws Exception {
Prototype prototype = new Prototype();
prototype.setBar(new Bar("bar"));
prototype.setId(3);
//浅拷贝
Prototype prototype1 = prototype.clone();
System.out.println("=== if any of the following varies, then it is shallow clone ===");
//基本类型是深拷贝
prototype1.setId(2);
System.out.println("the old id is 3, and now it is " + prototype.getId());
//引用类型是浅拷贝
prototype1.getBar().setName("newbar");
System.out.println("the old bar is {bar}, and now it is {" + prototype.getBar().getName()+"} <==");
//序列化来实现深拷贝
//Prototype 类以及 Bar 类都要实现 Serializable 接口
prototype.getBar().setName("bar"); //恢复旧名字
Prototype prototype2 = prototype.deepClone();
prototype2.getBar().setName("newbar");
System.out.println("the old bar is {bar}, and now it is {"+prototype.getBar().getName()+"}");
}
}
以下是输出结果
=== if any of the following varies, then it is shallow clone ===
the old id is 3, and now it is 3
the old bar is {bar}, and now it is {newbar} <==
the old bar is {bar}, and now it is {bar}
可以观察到: 进行浅拷贝后, 若通过新的对象改变对象内部的引用类型对象, 原有对象的内部的引用对象也会做出相同的改变. 之所以会发生这样的事情, 是因为此时两个对象内部包含的对象是同一个.
用序列化可以实现深拷贝.
在上面的代码中, Prototype类和Bar类都实现了了Serializable接口.
使用Prototype类中的deepClone方法得到的对象是深拷贝对象.
如果类的结构不至于太复杂, 对于引用型对象来说, 可以通过使用
new
关键字来实现深拷贝.