JAVA 引用类型深拷贝的三种实现方式
方法一:重写 clone()方法实现深拷贝(经典方式)
import java.util.ArrayList;
import java.util.List;class Address implements Cloneable {private List<String> citys;public Address(List<String> citys) {this.citys = citys;}// Getterpublic List<String> getCitys() {return citys;}/*** 重写clone()方法以实现深拷贝*/@Overridepublic Address clone() {try {// 1. 先调用super.clone()进行浅拷贝,得到一个新Address对象Address cloned = (Address) super.clone();// 2. 关键:对可变引用字段citys进行深拷贝// 创建一个新的ArrayList,并将原list中的所有元素复制进去if (this.citys != null) {cloned.citys = new ArrayList<>(this.citys); // 深拷贝的核心代码} else {cloned.citys = null;}// 3. 返回深拷贝后的对象return cloned;} catch (CloneNotSupportedException e) {// Cloneable已实现,理论上不会发生,抛出运行时异常throw new AssertionError(e);}}
}
关键点分析:
- new ArrayList<>(this.citys):这是实现深拷贝的核心。这个ArrayList的构造方法会创建一个新的List对象,并将原citys列表中的所有元素拷贝到新列表中。
- 由于列表中的元素是String(不可变对象),所以只需拷贝列表结构本身即可。如果元素也是可变对象,则需要递归地对每个元素进行深拷贝。
方法二:使用拷贝构造方法(更推荐)
这种方式通常被认为比clone()更清晰、更安全,也是Effective Java中推荐的方式。
class Address {private List<String> citys;public Address(List<String> citys) {this.citys = citys;}/*** 深拷贝构造方法* @param other 要被拷贝的原始对象*/public Address(Address other) {if (other != null) {if (other.citys != null) {// 创建新的List,拷贝所有元素this.citys = new ArrayList<>(other.citys); // 深拷贝核心} else {this.citys = null;}}}public List<String> getCitys() {return citys;}
}
方法三:使用序列化(适用于复杂对象图)
如果对象结构非常复杂,手动实现深拷贝会很繁琐。可以通过序列化(内存中)来实现深拷贝,但要求类实现 java.io.Serializable接口。
import java.io.*;class Address implements Serializable { // 1. 实现Serializable接口private List<String> citys;// ... 构造方法、getter .../*** 通过序列化实现深拷贝*/public Address deepCopy() {try (ByteArrayOutputStream bos = new ByteArrayOutputStream();ObjectOutputStream oos = new ObjectOutputStream(bos)) {// 将对象写入字节数组输出流oos.writeObject(this);oos.flush();try (ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());ObjectInputStream ois = new ObjectInputStream(bis)) {// 从字节数组输入流中读出新的对象return (Address) ois.readObject();}} catch (IOException | ClassNotFoundException e) {throw new RuntimeException("Deep copy failed", e);}}
}