Object.clone()默认不可用,因是protected方法且Object未实现Cloneable接口;需类显式实现Cloneable、重写public clone()并调用super.clone(),注意浅拷贝陷阱及深拷贝手动处理。

为什么 Object.clone() 默认不能直接用
因为 clone() 是 protected 方法,且 Object 类没实现 Cloneable 接口——直接调用会抛 CloneNotSupportedException。这不是设计缺陷,而是 Java 故意把“是否允许克隆”交由类自己声明。
实操建议:
- 目标类必须显式实现
Cloneable接口(空接口,仅作标记) - 重写
clone()方法,并改为public;内部调用super.clone() - 如果类含可变引用字段(如
ArrayList、自定义对象),默认的浅拷贝会导致原对象和副本共享同一份底层数据
浅拷贝 vs 深拷贝:什么时候必须手动处理
调用 super.clone() 只做浅拷贝:基本类型字段值复制,引用类型字段只复制地址。这意味着修改副本里的集合内容,原对象也会“感知”到。
常见错误现象:list.add("new") 后,原对象的 list 也多了一项。
立即学习“Java免费学习笔记(深入)”;
实操建议:
- 若字段是不可变类型(
String、Integer、LocalDateTime),不用额外处理 - 若字段是可变集合,需在
clone()方法里手动创建新实例,例如:new ArrayList(this.items) - 若字段是自定义对象且也支持克隆,应调用其
clone()方法,而非直接赋值
替代方案:为什么现在更推荐构造器或记录类
Cloneable 接口语义模糊,clone() 行为不强制约束,子类容易漏掉深拷贝逻辑,且反射调用 clone() 时无法静态检查。
使用场景:新项目、DTO、配置对象等轻量数据载体。
实操建议:
- 优先用构造器传入原对象:
new Person(other.getName(), other.getAge()) - JDK 14+ 可用
record类,天然不可变,复制即新建实例 - 需要灵活拷贝逻辑时,用静态工厂方法(如
Person.copyOf(person)),语义清晰、类型安全、便于加日志或校验
踩坑最多的三个地方
很多问题不是不会写,而是在边界情况里掉进默认行为的陷阱。
- 父类实现了
clone(),子类忘了调用super.clone()或覆盖它,结果克隆出父类字段但子类字段全为默认值(null/0) - 用了 Lombok 的
@Data,但没配@AllArgsConstructor或toBuilder = true,误以为自动生成了安全克隆逻辑 - 在 Spring 管理的 Bean 中调用
clone(),忽略了代理对象问题——实际克隆的是代理壳,不是目标实例
原型模式本身不复杂,难的是判断“哪里真需要克隆”,以及“克隆后谁负责维护一致性”。多数时候,明确构造比隐式 clone() 更可靠。







