重写 clone() 前必须实现 Cloneable 接口,否则调用时抛出 CloneNotSupportedException;clone() 默认浅拷贝,含可变引用需手动深拷贝;重写时须声明为 public 并 throws CloneNotSupportedException。

为什么重写 clone() 之前必须实现 Cloneable
不实现 Cloneable 接口就调用 clone(),会直接抛出 CloneNotSupportedException。这不是设计缺陷,而是 Java 的显式契约——Cloneable 是个空标记接口,仅用于告诉 JVM:“这个类允许被克隆”。JVM 在 Object.clone() 底层会检查该标记,没找到就拒绝执行。
常见错误是只重写 clone() 却忘了 implements Cloneable,结果运行时报错,但编译不报错,容易漏查。
clone() 默认只做浅拷贝,深拷贝得自己动手
Object.clone() 默认行为是字段级逐位复制:基本类型值照搬,引用类型只复制地址。这意味着原始对象和克隆体共享同一份内部对象引用,改一个会影响另一个。
- 如果类里只有
int、String(不可变)、LocalDateTime(不可变)这类字段,浅拷贝通常够用 - 一旦包含可变的引用类型(如
ArrayList、自定义 POJO、数组),就必须手动深拷贝这些字段 - 注意:
String虽是引用类型,但因不可变性,浅拷贝完全安全,不用额外处理
示例:
public class Person implements Cloneable {
private String name;
private ArrayList<String> hobbies;
@Override
protected Object clone() throws CloneNotSupportedException {
Person cloned = (Person) super.clone();
// hobbies 是可变引用,必须深拷贝
cloned.hobbies = new ArrayList<>(this.hobbies); // 浅拷贝 ArrayList 本身
return cloned;
}
}
重写 clone() 必须声明为 public 并处理异常
Object.clone() 是 protected 且抛出 CloneNotSupportedException。子类重写时若不提升访问级别,外部代码根本调不到;若不处理异常,编译失败。
- 必须声明为
public Object clone()(或带泛型返回,如public Person clone()) - 必须捕获或声明抛出
CloneNotSupportedException;推荐在方法签名中throws CloneNotSupportedException,让调用方明确感知风险 - 不要在方法体内吞掉异常(比如空
catch),否则克隆失败却静默返回null或旧对象,极难排查
数组、集合、嵌套对象的深拷贝容易漏掉层级
深拷贝不是“递归调用 clone()”就能一劳永逸的事。很多类(如 ArrayList、HashMap)本身没实现 Cloneable,它们的 clone() 方法只复制容器结构,不保证元素深拷贝。
- 对
ArrayList<person></person>,new ArrayList(original)只是新容器 + 原引用,里面每个Person仍是原对象 - 正确做法:遍历并逐个调用元素的
clone()(前提是元素也实现了Cloneable) - 遇到第三方类或 final 类(如
BigDecimal),无法调clone(),得用构造器或静态工厂方法重建
真正可靠的深拷贝往往要结合序列化、JSON 工具(如 Jackson)或专门的库(如 Apache Commons Lang 的 SerializationUtils),而不是死磕 clone() 链。
实际项目里,clone() 很少被完整用起来,因为深拷贝逻辑分散、易错、难以维护。真需要深拷贝时,多数人转向更可控的方式——比如构造器传参、Builder 模式或序列化反序列化。

















