Java中clone()默认浅拷贝,需手动实现深拷贝:必须实现Cloneable接口并重写public clone()方法,调用super.clone()后对可变引用字段逐个深拷贝,注意循环引用、final字段及资源句柄问题;推荐使用拷贝构造器、静态工厂或序列化替代。

Java 中 clone() 方法默认只做浅拷贝,直接调用不会防止数据篡改——副本和原对象共享引用类型字段,改一个,另一个跟着变。要真正防止数据被意外修改,必须手动实现深拷贝,切断所有可变引用的共享链。
必须实现 Cloneable 并重写 public clone()
这是基础门槛,缺一不可:
- 类声明
implements Cloneable,否则调用clone()会抛CloneNotSupportedException - 重写方法签名必须是
public Object clone()(Java 5+ 可协变返回,如public Person clone(),但底层仍需强转) - 第一行必须调用
super.clone()获取原始字段副本,它只复制字段值,不处理引用逻辑
对每个可变引用字段单独深拷贝
基本类型(int、boolean)和不可变类(String、LocalDateTime、Integer)可直接赋值;但以下类型必须主动断开引用:
-
集合类:不能用
list.clone()(仍是浅的),应新建实例,如new ArrayList(originalList)或originalList.stream().map(Object::clone).collect(Collectors.toList()) -
自定义对象:若该类也支持克隆,调用其
clone();否则用构造器或静态工厂重建,例如new Address(src.getAddress().getCity()) -
数组:
array.clone()只复制引用,元素未克隆;需遍历每个元素调用clone()或新建对象 -
第三方类未实现 Cloneable:无法调
field.clone(),改用构造参数重建、JSON 序列化反序列化,或 Apache Commons Lang 的SerializationUtils.clone()
警惕循环引用与 final 字段
手动深拷贝时容易踩坑:
立即学习“Java免费学习笔记(深入)”;
- 若 A 引用 B,B 又引用 A,递归调
clone()会导致栈溢出;可引入缓存 Map 记录已克隆对象,避免重复处理 -
final字段在super.clone()后无法重新赋值(编译报错),此时clone()不适用,应改用构造器拷贝或 builder 模式 - 含资源句柄(如
FileInputStream、数据库连接)的对象不适合克隆,深拷贝可能引发资源冲突或泄漏
更安全的替代方案
因为 clone() 机制本身有缺陷(绕过构造器、类型不安全、与 final 冲突),多数现代项目倾向避开它:
-
拷贝构造器:定义
Person(Person src),显式复制每个字段,逻辑清晰、类型安全、支持final -
静态工厂方法:如
Person.copyOf(src),封装深拷贝逻辑,便于统一维护 -
序列化方式:要求所有字段可序列化,用
ObjectOutputStream+ByteArrayInputStream实现通用深拷贝,适合结构稳定、无外部资源的对象


















