Java深拷贝需切断所有共享引用链:手动递归克隆(重写clone()并逐个克隆引用字段)、序列化反序列化(要求全量Serializable)或拷贝构造器;final字段需特殊处理,不可变类型可跳过深拷贝。

Java 中默认的 clone() 方法只做浅拷贝,要实现真正的深拷贝,必须手动处理所有引用类型字段,确保它们也生成独立副本。核心不是“改一个方法”,而是“切断所有共享引用链”。
手动递归克隆:最可控、最常用的方式
在重写的 clone() 方法中,对每个可变的引用字段显式调用其 clone() 方法(前提是该字段类型也实现了 Cloneable 并重写了 clone())。
- 父类需实现
Cloneable接口,且clone()方法声明为public - 先调用
super.clone()得到浅拷贝对象 - 再逐个为引用字段赋新值:比如
cloned.address = (Address) this.address.clone(); - 如果字段是集合(如
List),不能直接list.clone()(那是浅的),而要新建集合并逐个克隆元素 - 遇到
null字段需判空,避免NullPointerException
序列化反序列化:通用但有约束
利用 Java 原生序列化机制,将对象写成字节流再读回来,天然实现完整对象图重建。
在调试 Java 后端 API 故障时使用此技能,尤其适用于以下任务:分析日志、跨 Controller、Service 和 Mapper 层追踪请求、定位异常根因、检查 SQL 执行或参数流转,以及生成简洁的诊断结论与修复方案。
- 要求目标类及其所有字段类型(包括嵌套类、集合元素类型)都实现
Serializable - 静态字段、
transient字段不会被序列化,因此也不会出现在副本中 - 性能开销较大,不适合高频调用场景
- 代码简洁,适合结构复杂但改动少的配置类、DTO 类
- 第三方库如 Apache Commons Lang 的
SerializationUtils.clone()就是封装了这一逻辑
注意 final 字段和不可变类型的处理
final 字段在 clone() 过程中无法直接赋值(编译报错),必须在构造时初始化;而 String、Integer 等不可变类型,即使浅拷贝也是安全的——因为它们的值无法被修改,无需深拷贝。
- 若引用字段是
final Address address,就不能在clone()里写cloned.address = ... - 解决办法:用拷贝构造器替代
clone(),或把字段改为非final(权衡设计意图) - 不可变对象可放心复用,深拷贝时跳过它们能提升效率
避免常见陷阱
深拷贝容易漏掉某一层引用,导致“伪深拷贝”——表面看没问题,运行时却因某个字段共享而出错。
- 测试时不仅要检查顶层字段是否独立,还要修改嵌套对象(如
user.getAddress().setCity("X")),再验证原对象是否受影响 - 循环引用(A 引用 B,B 又引用 A)会导致序列化失败或栈溢出,手动克隆需加缓存机制记录已克隆对象
- 使用 IDE 自动生成的
clone()代码通常只含super.clone(),务必人工补全引用字段逻辑
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南










