防止封装属性深拷贝产生副作用,关键是切断原始对象与副本间的可变引用链,需对可变引用类型(如POJO、集合)深拷贝,不可变类型(String、LocalDateTime等)直接赋值;推荐使用拷贝构造函数并处理循环引用缓存;优先采用不可变设计从源头规避问题。

防止封装属性深拷贝时产生副作用,核心是切断原始对象与副本之间的可变引用链。不是“只要复制了就行”,而是要确保所有可变字段都生成独立实例,且后续修改只作用于副本本身。
明确哪些字段必须深拷贝
不可变类型(如 String、Integer、LocalDateTime、BigDecimal)无需深拷贝,直接赋值即可;真正需要深拷贝的是可变引用类型——比如自定义 POJO、集合、数组、含 setter 的嵌套对象。例如 User 类中若包含 Occupation oc 字段,而 Occupation 有 setDepartment() 方法,那就必须为 oc 创建新实例并逐字段复制,否则两个 User 共享同一 Occupation,改一个就影响另一个。
用拷贝构造函数替代简单赋值
避免使用 c = a 或 BeanUtils.copyProperties() 这类默认浅拷贝方式。推荐为关键类显式定义拷贝构造函数:
- public User(User other) {
- this.name = other.name; // String,安全
- this.oc = new Occupation(other.oc); // 深拷贝可变字段
- }
这样语义清晰,控制力强,不依赖反射或运行时配置,也不受工具类默认行为干扰。
处理嵌套与循环引用
当对象图存在 A→B→A 这类结构时,递归深拷贝会触发 StackOverflowError。解决方法是引入缓存机制:
- 用 IdentityHashMap 记录“原始对象 → 克隆对象”映射
- 每次克隆前先查缓存,命中则直接返回,不再递归
- 对每个引用字段,先判空、再查缓存、最后决定是否新建并递归
- 基本类型和不可变对象跳过缓存,直接赋值
该策略适用于手动实现的 deepCopy 方法,也兼容 JSON 序列化等替代路径(但要注意序列化对 transient 和 final 字段的限制)。
优先采用不可变设计
从源头减少深拷贝需求:把类声明为 final,字段设为 final,去掉所有 setter,仅通过构造函数初始化,修改时返回新实例(如 addReal() 返回新 ComplexNumber)。这样对象天然线程安全、无共享状态风险,也就不必纠结“该不该深拷贝”。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











