Java中Object.clone()默认是浅拷贝:基本类型和不可变对象(如String)字段被复制,引用类型字段仅复制地址,新旧对象共享同一堆内存中的可变对象实例。

clone() 默认就是浅拷贝,不用额外配置
Java 中所有类都继承自 Object,而 Object.clone() 的行为天然就是浅拷贝:基本类型字段值被复制,引用类型字段只复制地址,新旧对象共享同一堆内存中的对象实例。你不需要写任何逻辑来“开启”浅拷贝——只要正确调用它,它就自动是浅的。
但必须满足三个硬性条件,否则会直接失败:
- 目标类要实现
Cloneable接口(空接口,仅作标记) - 重写
clone()方法,并声明为public(因为父类中是protected) - 在重写方法里调用
super.clone()
漏掉任意一条,要么编译报错,要么运行时抛出 CloneNotSupportedException。
验证浅拷贝的关键:修改引用字段是否影响原对象
光看定义容易绕晕,动手验证最直接。比如有一个类包含一个 String 字段和一个 ArrayList 字段:
public class Person implements Cloneable {
public String name;
public ArrayList<string> hobbies;
public Person(String name, ArrayList<string> hobbies) {
this.name = name;
this.hobbies = hobbies;
}
@Override
public Person clone() {
try {
return (Person) super.clone();
} catch (CloneNotSupportedException e) {
throw new RuntimeException(e);
}
}
}</string></string>
测试时注意两点:
-
name是不可变对象,修改它(如赋新值)不会波及原对象,这是表象,不是浅拷贝的本质 - 真正能暴露浅拷贝的是对
hobbies的操作:cloned.hobbies.add("swim")会导致original.hobbies也多出一项 - 因为
cloned.hobbies和original.hobbies指向同一个ArrayList实例
哪些类型看起来像深拷贝,其实是浅拷贝的假象
常见误解是:“String 被改了不影响原对象,所以 clone 是深的”。错。这是因为 String 不可变,你所谓的“修改”,本质是重新赋值了一个新字符串引用,原引用没动。真正考验浅拷贝的是可变引用类型:
-
ArrayList、HashMap、自定义可变对象(如含 public 字段的Address类) - 数组(
int[]、String[])也是引用类型,clone()后仍共享底层数组 - 注意
Integer、Boolean等包装类在自动装箱/拆箱下表现类似基本类型,但它们仍是对象,只是 JVM 做了缓存优化
别被不可变性骗了——浅拷贝的判定依据永远是“引用是否指向同一块堆内存”,不是“改了之后看不出来”。
为什么不能直接靠 clone() 做通用深拷贝
想靠重写 clone() 把所有引用字段都递归 clone() 一遍?理论上可行,但实际踩坑极多:
- 字段类型未必都实现了
Cloneable,比如第三方库的类、java.time.LocalDateTime就不支持 - 循环引用会导致无限递归(A 引用 B,B 又引用 A)
- 某些类的
clone()行为未定义或被禁用(如Thread、Socket) - 手动维护深拷贝逻辑极易遗漏字段,尤其后续新增引用字段时
真需要深拷贝,优先考虑序列化(ObjectOutputStream)、JSON 序列化(Gson/Fastjson)、或构造器传参方式。把 clone() 当作浅拷贝专用工具更稳妥。










