Object.clone()不能直接调用,因为它是protected方法且要求类实现Cloneable接口,否则运行时抛CloneNotSupportedException;默认为浅拷贝,引用类型仅复制地址;JDK17+已标记为@Deprecated。

Object.clone() 为什么不能直接调用
因为 clone() 是 protected 方法,子类无法直接在外部调用,除非当前类显式重写并改为 public。更关键的是,Object 类的默认实现会检查调用者是否实现了 Cloneable 接口——没实现就抛 CloneNotSupportedException,不是返回 null 或静默失败。
常见错误现象:myObj.clone() 编译通过但运行时报 java.lang.CloneNotSupportedException,根源往往就是忘了声明接口或漏了重写。
- 必须实现
Cloneable接口(它是个空标记接口,但 JVM 会特殊识别) - 必须重写
clone()方法,并设为public - 重写时建议调用
super.clone(),而不是手动 new + 赋值(否则失去浅拷贝语义)
浅拷贝的实际效果和典型陷阱
Object.clone() 默认只复制对象自身字段,对引用类型字段仅复制引用地址,不递归克隆其指向的对象。这意味着原始对象和克隆对象共享内部可变对象。
使用场景:适合字段全是基本类型、不可变对象(如 String、Integer),或你明确希望共享引用的情况。
容易踩的坑:
- 如果类里有
ArrayList字段,克隆后两个对象的list指向同一实例,修改其中一个的 list 内容会影响另一个 - 若字段是自定义可变类(比如
Person address),且该类没实现深拷贝逻辑,address会被共享 -
Cloneable不强制要求重写clone(),但没重写就等于没开放克隆能力
正确实现 Cloneable 的最小可行代码
以下是最简但完整的实现,注意访问修饰符、异常声明和调用链:
public class User implements Cloneable {
private String name;
private int age;
private ArrayList<String> tags;
@Override
public User clone() {
try {
return (User) super.clone(); // 浅拷贝本体
} catch (CloneNotSupportedException e) {
throw new AssertionError("Cloneable interface is implemented, this should never happen", e);
}
}
}
说明:
-
super.clone()已完成字段逐位复制,包括tags引用本身(不是tags里的元素) - 若需深拷贝
tags,得在clone()里手动new ArrayList(this.tags)替换 - 不要在
clone()中调用构造函数,那会破坏克隆语义
Cloneable 接口没有方法却必不可少的原因
Cloneable 是 JVM 层面的“通行证”。Object.clone() 在底层会检查 getClass().isInterface() 和类的标记位,不是靠编译期语法校验。所以即使你删掉 implements Cloneable,只保留 public Object clone() 方法,运行时仍会抛异常。
性能与兼容性影响:
- 浅拷贝开销极小,比序列化或反射拷贝快得多
- 但 JDK 17+ 中
Cloneable已被标记为@Deprecated(forRemoval = true),官方倾向用构造器、记录类(record)或copyOf()风格 API 替代 - 第三方库(如 Apache Commons Lang 的
BeanUtils.cloneBean())底层可能仍依赖它,兼容性需留意
真正容易被忽略的是:即使所有字段都不可变,只要类声明了 Cloneable 并重写了 clone(),就必须确保子类也能安全克隆——比如继承链中某父类没重写 clone(),子类调用 super.clone() 就会失败。

















