浅拷贝仅复制对象本身,引用字段共享同一实例;深拷贝递归复制整个对象图。面试重在考察对引用传递本质的理解及规避共享副作用的能力。

浅拷贝只复制对象本身,不复制它引用的其他对象;深拷贝会递归复制整个对象图,包括所有嵌套引用的对象。面试中问这个,核心是看你是否理解引用传递的本质,以及能否避开常见陷阱。
浅拷贝:clone() 方法默认行为
Java 中 Object.clone() 默认实现的是浅拷贝 —— 它会创建一个新对象,并逐字段复制原始对象的值。对基本类型字段没问题,但对引用类型字段,只是把引用地址复制过去,新旧对象仍共享同一份堆内存中的对象。
要让类支持 clone(),必须:
- 实现 Cloneable 接口(仅作标记,不提供方法)
- 重写 clone() 方法并声明为 public
- 在方法内调用 super.clone()
public class Person implements Cloneable {
private String name;
private Address address; // 引用类型
@Override
public Person clone() {
try {
return (Person) super.clone(); // 浅拷贝:address 字段引用未变
} catch (CloneNotSupportedException e) {
throw new RuntimeException(e);
}
}}
此时修改 person1.address.city,person2.address.city 也会变 —— 这就是典型的浅拷贝副作用。
立即学习“Java免费学习笔记(深入)”;
深拷贝:手动处理引用字段或序列化绕过
深拷贝没有通用银弹。最稳妥的方式是在 clone() 中对每个引用字段手动调用其自身的 clone() 或构造新实例:
@Override
public Person clone() {
try {
Person cloned = (Person) super.clone();
if (this.address != null) {
cloned.address = this.address.clone(); // 假设 Address 也实现了 Cloneable
}
return cloned;
} catch (CloneNotSupportedException e) {
throw new RuntimeException(e);
}
}另一种方式是利用序列化(前提是所有字段可序列化):
public static T deepClone(T obj) {
try (ByteArrayInputStream bis = new ByteArrayInputStream(
new ObjectOutputStream(new ByteArrayOutputStream()).writeObject(obj)));
ObjectInputStream ois = new ObjectInputStream(bis)) {
return (T) ois.readObject();
} catch (Exception e) {
throw new RuntimeException(e);
}
}
注意:serialVersionUID 缺失、含不可序列化字段(如 Thread、Socket)、性能开销大,都是序列化深拷贝的实际限制。
Arrays.copyOf() 和 Collections.copy() 不是深拷贝
这两个方法常被误认为能做深拷贝,其实它们都只做一层复制:
- Arrays.copyOf(arr, len) 对 int[] 是深拷贝(基本类型),但对 Person[] 只是复制了引用数组,每个元素仍指向原对象
- Collections.copy(dest, src) 同理,只复制引用,不递归克隆元素内容
如果源集合里存的是可变对象,后续修改元素状态,目标集合看到的也是同一份状态 —— 面试时混淆这点,基本就暴露了对“引用”理解不扎实。
真正难的不是写出一个能跑的深拷贝,而是判断哪些字段需要深拷贝、哪些可以共享、哪些根本不能拷贝(比如回调监听器、数据库连接)。实际工程中,多数场景应优先用不可变对象设计,而不是在拷贝逻辑上反复打补丁。










