Object.clone()默认浅拷贝,防止状态污染需在重写clone()中手动深拷贝可变引用(如ArrayList、自定义POJO),而String、Integer等不可变对象可直接赋值;类须实现Cloneable、clone()改为public并调super.clone(),且嵌套可变类型也需支持克隆。

Java 中 Object.clone() 本身只做浅拷贝,无法自动避免引用对象被篡改。要真正隔离修改、防止状态污染,必须在重写的 clone() 方法中**手动切断所有可变引用的共享链**——不是靠“启用某个开关”,而是逐字段判断、有选择地深拷贝。
必须满足的三个前提条件
缺一不可,否则连浅拷贝都会失败:
- 当前类声明
implements Cloneable(仅标记,不提供方法) -
clone()方法改为public,且第一行必须调用super.clone() - 所有需要深拷贝的嵌套引用类型(如
Address、ArrayList)也得各自实现Cloneable并重写public clone()
哪些字段要深拷贝,哪些可以直接用
关键在区分「可变」与「不可变」:
你接手一个需求时,是不是经常这样:"改一个小字段 → 结果把不相关的代码也改了"、"需求说得很模糊 → 做着做着发现跑偏了"、"数据库加个字段 → 上线才发现漏了数据兼容"、"做了个新功能 → 一个月后自己都忘了当时怎么设计的"、"写到一半被中断 → 再回来不知道从哪继续";这个 Skill 就是专门解决这些问题的。 它会自动探测你的项目环境(JDK、Spring Boot、ORM、数据库、前端框架),记在
-
不用处理(直接赋值即可):基本类型(
int、boolean)、不可变类(String、LocalDateTime、Integer、BigDecimal)——它们的“修改”本质是新建对象,原引用不受影响 -
必须深拷贝:自定义 POJO(如
Address)、集合(ArrayList、HashMap)、数组(尤其是对象数组)、Date、StringBuilder等——这些对象内部状态可被外部 setter 或方法改变,共享即风险
典型字段的深拷贝写法
以一个含 Address 和 List<String> 的 Person 类为例:
立即学习“Java免费学习笔记(深入)”;
-
address字段:if (this.address != null) cloned.address = this.address.clone();(前提是Address类已正确实现clone()) -
hobbies(List<String>):cloned.hobbies = new ArrayList(this.hobbies);(元素是String,不可变,构造新集合即可) -
orders(List<Order>,Order可变):cloned.orders = this.orders.stream().map(Order::clone).collect(Collectors.toList()); -
photos(Photo[]数组):cloned.photos = Arrays.stream(this.photos).map(Photo::clone).toArray(Photo[]::new);(不能只写this.photos.clone(),那是浅的)
绕不开的坑和更稳的选择
手动克隆容易踩到硬伤:
-
循环引用(A→B→A):递归调
clone()会栈溢出,需用IdentityHashMap<Object, Object>缓存已克隆对象 -
final 字段:
super.clone()后无法重新赋值,编译报错;此时clone()机制失效,必须换方案 -
第三方类未实现 Cloneable(如
java.time.ZonedDateTime):不能调.clone(),只能用构造器重建或序列化 -
推荐替代方案:拷贝构造器(
public Person(Person src))或静态工厂(Person.copyOf(src))——逻辑清晰、支持final、不绕过构造逻辑、类型安全

















