Java中直接实现Cloneable接口并重写public clone()方法是最轻量的原生克隆方式,但默认仅支持浅拷贝:基本类型和String被复制,引用类型仅复制地址;必须实现Cloneable接口以避免运行时抛出CloneNotSupportedException异常。

直接实现 Cloneable 接口并重写 clone() 方法,是 Java 中最轻量、原生支持的克隆方式,但必须清楚它默认只做浅拷贝——基本类型值被复制,引用字段只是地址拷贝,不新建对象。能否安全使用,关键看你怎么处理引用字段。
必须实现 Cloneable 接口,否则 clone() 会运行时报错
Cloneable 是一个空标记接口,没有方法,也不参与逻辑实现。它的唯一作用是向 JVM 发出“许可信号”:这个类允许被克隆。如果没声明 implements Cloneable,哪怕重写了 clone() 方法,调用时仍会抛 CloneNotSupportedException——编译能过,运行必崩。
- 仅加
implements Cloneable不够,必须同时重写clone() - 子类继承自未实现
Cloneable的父类时,即使自己实现了,调用super.clone()也可能失败 - 接口本身不提供任何方法,不会自动注入
clone(),一切靠手动重写
正确重写 clone() 的三个硬性动作
规范的 clone() 方法要同时满足可访问性、异常处理和语义正确性:
- 声明为 public(
Object.clone()是protected,不改访问级别就无法外部调用) - 调用
super.clone()获取基础副本(这是浅拷贝的起点) - 捕获或包装
CloneNotSupportedException,推荐转为RuntimeException或AssertionError(既然已实现接口,该异常理论上不应发生)
返回类型建议用具体类名(如 Person clone()),避免调用方每次强转。
浅拷贝够不够?取决于字段是否可变
默认行为下:
– int、boolean、String 等不可变类型,安全复用;
– ArrayList、Date、StringBuilder、自定义可变对象等,仍是共享引用,一改全改。
-
String安全是特例,不是因为深拷贝,而是因为不可变——不能掩盖它是浅拷贝的事实 -
ArrayList.clone()返回新容器,但元素还是原引用;若元素是User对象,修改其属性会影响原列表 -
Date必须手动new Date(originalDate.getTime())或调用clone(),否则副本时间变化即原对象变化
需要深拷贝时,就在 clone() 里逐字段处理
不要指望 super.clone() 自动递归克隆。可靠路径是:先浅拷贝打底,再对每个可变引用字段单独克隆。
- 集合类:新建容器,再遍历填充(如
cloned.list = new ArrayList(this.list)) - 嵌套对象:确保其类也实现
Cloneable,然后显式调用field.clone() - 第三方类或无源码类:无法调用
clone(),改用构造器传参、JSON 序列化或Serializable方式替代
例如:Address address 字段需写成 cloned.address = this.address.clone(),而不是简单赋值。
更推荐的替代方案,尤其对复杂对象
纯靠 Cloneable 易漏字段、难维护,且破坏封装(暴露内部结构供克隆逻辑读取)。现代开发中,以下方式更清晰可控:
-
拷贝构造器:如
public Person(Person other) { this.name = other.name; this.address = new Address(other.address); } -
静态工厂方法:如
public static Person copyOf(Person src) { ... } - Builder 模式:适合字段多、可选参数多的对象
- 序列化/反序列化(慎用):能全自动深拷贝,但性能低、要求所有字段可序列化、可能引入安全与兼容问题
这些方式不依赖 JVM 特殊机制,语义明确,测试友好,调试直观。

















