Java中clone()抛CloneNotSupportedException因Object.clone()是protected且声明受检异常;必须实现Cloneable接口并重写为public,且重写时须调用super.clone()以触发JVM内存拷贝。

Java中clone()方法为什么总抛CloneNotSupportedException
因为Object.clone()是protected且声明了受检异常,直接调用必然报错。必须满足两个前提才能用:类实现Cloneable接口,且重写clone()为public。
-
Cloneable只是标记接口,不提供任何方法;不实现它,super.clone()会直接抛CloneNotSupportedException - 重写时必须调用
super.clone(),否则无法触发JVM底层的内存拷贝逻辑 - 即使实现了
Cloneable,若父类没重写clone()或未声明public,子类仍可能因访问权限失败
public class Person implements Cloneable {
private String name;
private Address address; // 假设Address是可变引用类型
@Override
public Object clone() throws CloneNotSupportedException {
return super.clone(); // 浅拷贝:address引用被复制,不是新对象
}
}
浅拷贝和深拷贝在字段赋值上的本质区别
浅拷贝只复制对象本身字段的值:基本类型值被复制,引用类型字段的地址值被复制(即新旧对象指向同一堆内存);深拷贝要求所有引用字段也递归创建新实例。
- 数组字段:
int[]浅拷贝后是独立副本(因为int是基本类型),但String[]或Person[]浅拷贝后数组元素仍共享引用 - 包装类如
Integer、String看似“安全”,实则是不可变对象,所以浅拷贝不会引发副作用,但不能因此误判为深拷贝 - 含
final引用字段的类,若该引用指向可变对象(如final List),浅拷贝后仍可通过该引用修改原对象状态tags
手动实现深拷贝的三种可靠方式及适用场景
没有银弹。选择取决于对象结构复杂度、性能敏感度、是否允许第三方依赖。
- 逐字段
clone():适用于字段少、引用类型明确且都支持clone()的场景。需确保每个引用字段类型自身已正确实现深拷贝逻辑 - 序列化反序列化(如
ByteArrayOutputStream + ObjectInputStream):能自动处理任意嵌套结构,但要求所有字段类型可序列化,且性能开销大,不适合高频调用 - JSON序列化(如Jackson
ObjectMapper):绕过Serializable限制,对POJO友好;但会丢失类型信息(如泛型擦除)、忽略transient和非getter/setter字段,且无法处理循环引用
ObjectMapper mapper = new ObjectMapper();
Person original = new Person("Alice", new Address("Beijing"));
try {
Person deepCopy = mapper.readValue(mapper.writeValueAsBytes(original), Person.class);
} catch (Exception e) {
throw new RuntimeException(e);
}
使用BeanUtils.copyProperties()做克隆的陷阱
Spring的BeanUtils.copyProperties()不是克隆工具,而是属性拷贝工具——它只通过反射调用getter/setter,不创建新对象,也不处理嵌套对象,更不保证线程安全。
立即学习“Java免费学习笔记(深入)”;
- 目标对象必须预先实例化,否则空指针;源/目标字段名和类型需严格匹配,否则静默跳过
- 遇到集合字段(如
List),只会把整个集合对象引用赋过去,不是复制元素内容 - 若字段是
private final且无setter,该字段完全不会被复制;有setter但setter里做了校验或业务逻辑,拷贝过程可能触发副作用
真正需要克隆时,别把它当clone()替代品。它适合DTO转换,不适合对象隔离。










