java中object类的clone()默认为浅拷贝,修改克隆体的可变引用会污染原对象;需在重写clone()中对可变引用字段(如list、自定义对象、对象数组)显式深拷贝,而基本类型和不可变类无需处理;推荐使用拷贝构造器或静态工厂方法替代clone()。

Java 中 Object 类的 clone() 方法默认只做浅拷贝,直接调用 super.clone() 不会复制引用字段指向的对象,因此修改克隆体中的可变引用(如 List、自定义对象等),原对象状态会被连带改变——这就是“状态被污染”的典型表现。要防止这种情况,必须在重写的 clone() 方法中对每个可变引用字段显式做深拷贝。
明确哪些字段需要深拷贝
不是所有字段都要处理:
-
基本类型(
int、boolean等)和不可变类(String、LocalDateTime、Integer等):值直接复制或共享无副作用,无需额外操作 -
可变引用类型(
ArrayList、HashMap、自定义类实例等):必须重建副本,否则两个对象共用同一堆内存中的对象 -
数组(尤其是对象数组):
array.clone()仍是浅的,需遍历元素逐个克隆或用Arrays.copyOf()配合新构造
手动实现深拷贝的三种常用方式
核心原则:在 clone() 方法中拿到 super.clone() 的结果后,替换掉所有可变引用字段的值。
-
用构造器或工厂方法新建副本:适用于第三方类或未实现
Cloneable的字段
例如:cloned.hobbies = new ArrayList(this.hobbies); -
调用字段自身的
clone():要求该字段所属类也实现了Cloneable并重写了public clone()
例如:cloned.address = this.address.clone(); -
对数组逐元素处理:避免
addresses.clone()只复制地址
例如:cloned.addresses = Arrays.stream(this.addresses).map(Address::clone).toArray(Address[]::new);
注意循环引用与 final 字段限制
深拷贝过程中容易踩坑:
-
循环引用(A→B→A):手动递归克隆可能栈溢出,需加缓存(如
Map<object object></object>记录已克隆对象)或改用序列化 -
final 引用字段:
super.clone()无法在 clone 方法中重新赋值,编译报错;要么去掉final,要么改用拷贝构造器替代clone() -
异常处理:必须声明
throws CloneNotSupportedException或捕获后包装为运行时异常,不能静默吞掉
更推荐的替代方案
因为 clone() 机制本身有设计缺陷(破坏封装、与 final 冲突、类型不安全),多数现代项目倾向避开它:
-
拷贝构造器:清晰、可控、支持
final字段,例如public Person(Person other) { this.name = other.name; this.address = new Address(other.address); } -
静态工厂方法:语义更明确,如
Person.copyOf(original) -
序列化反序列化:适合结构复杂但全可序列化的场景,不过性能低、有
Serializable约束,且不支持 Lambda、线程局部变量等
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











