正确重写 clone() 需满足两个前提:类实现 Cloneable 接口且 clone() 方法为 public;super.clone() 仅返回浅拷贝,含可变引用字段时须手动深拷贝,并注意 final 字段、循环引用及第三方类限制。

正确重写 clone() 方法不是简单加个 public 修饰符再调用 super.clone() 就完事。它是一套有约束、需分层处理的机制,核心目标是让克隆行为符合预期——尤其是当对象含引用字段时。
必须满足两个前提条件
缺一不可,否则运行时直接失败:
- 类必须显式声明
implements Cloneable—— 这是个标记接口,不提供任何方法,但 JVM 会检查它;不写就调clone(),必定抛CloneNotSupportedException -
clone()方法必须重写为public(不能是protected),且返回类型建议协变(如public Person clone()),避免外部强转
super.clone() 只是起点,不是终点
super.clone() 是 native 实现,只做字段级复制:基本类型值拷贝,引用类型地址拷贝。它返回的是一个浅拷贝结果,后续必须手动干预:
- 如果类中所有字段都是基本类型或不可变对象(如
String、LocalDateTime、Integer),那直接返回super.clone()就够了 - 只要存在可变引用字段(如自定义对象、
ArrayList、Date、StringBuilder),就必须在super.clone()返回后,逐个处理
对引用字段做深拷贝的关键操作
不是统一调一次 .clone() 就行,要按字段类型区别对待:
立即学习“Java免费学习笔记(深入)”;
- 自定义类字段(如
Address address):确保该类也实现了Cloneable并重写了public clone(),然后在本类clone()中写cloned.address = this.address.clone(),注意判空 - 集合字段(如
List<Item> items):items.clone()仍是浅的!应新建集合,遍历原集合,对每个可变元素调clone()或用构造器重建,例如:cloned.items = new ArrayList<>(this.items.stream().map(Item::clone).collect(Collectors.toList())); - 数组字段:对象数组不能直接
array.clone(),需新建数组并循环克隆每个元素;基本类型数组(如int[])可用Arrays.copyOf()
绕不开的细节和常见陷阱
这些地方不注意,克隆看似成功,实则埋下共享修改的隐患:
-
final字段在super.clone()后无法重新赋值,导致无法完成深拷贝——此时应放弃clone(),改用拷贝构造器 - 循环引用(A→B→A)会导致递归调用栈溢出,需引入缓存(如
Map<Object, Object>)记录已克隆对象 - 第三方类未实现
Cloneable(如某些 SDK 对象),强行.clone()会抛异常,应改用构造参数重建、JSON 序列化或SerializationUtils.clone() - 异常处理必须到位:虽然
CloneNotSupportedException在Cloneable类上不会抛,但子字段克隆时可能抛,建议用try-catch包裹具体克隆语句,而非整个方法


















