Java中clone()默认浅拷贝,基本类型和不可变类安全,但可变引用类型(如集合、自定义对象、数组元素)共享实例,易引发隐蔽Bug;深拷贝需手动处理引用字段或改用拷贝构造器等更优方案。

Java 中 clone() 方法默认只做浅拷贝,表面看是“复制了一个新对象”,但内部引用字段仍共享原对象实例——这正是多数隐蔽 Bug 的源头。
浅拷贝的变量风险:基本类型安全,引用类型危险
浅拷贝对基本类型(int、boolean 等)和不可变类(如 String、LocalDateTime)是安全的:值被独立复制,互不影响。但一旦字段是可变引用类型,风险立即浮现:
-
集合类(
List/Map/Set):两个对象共用同一个ArrayList实例,往副本里add(),原对象的列表也跟着变; -
自定义对象(如
Address、OrderDetail):副本修改其属性(如address.setCity("Shanghai")),原对象看到的也是新城市; -
数组(
int[]、String[]):数组本身被复制(新地址),但数组元素若为对象引用,仍指向原实例——即“数组浅,元素更浅”。
深拷贝未落实时的典型故障现象
你以为重写了 clone() 就万事大吉?实际中常见“伪深拷贝”,表现为:
- 调用了
super.clone(),但没处理任何引用字段——仍是纯浅拷贝; - 对
List用了new ArrayList(original),但 list 里存的是自定义对象,这些对象本身没被克隆; - 字段是第三方类(如
org.apache.commons.lang3.time.DateUtils返回的对象),既没实现Cloneable,也没提供拷贝构造器,直接field.clone()报错或跳过; - 存在循环引用(A→B→A),手动递归深拷贝时未设 visited 缓存,导致栈溢出或无限循环。
规避风险的关键动作
不依赖 clone() 机制本身,而是聚焦字段级控制:
-
明确标记可变引用字段:如
private List、permissions private Date createTime,它们必须被显式重建; -
基本类型和不可变类可直赋:无需额外操作,
cloned.age = this.age、cloned.name = this.name即可; -
集合优先用构造器新建:如
cloned.permissions = new ArrayList(this.permissions)——前提是Permission自身不可变;若可变,需遍历stream().map(Permission::deepCopy).collect(...); -
日期等可变类务必重建:不要
cloned.createTime = this.createTime,而应cloned.createTime = new Date(this.createTime.getTime())或用Instant+DateTimeFormatter转换。
比 clone() 更稳的替代方案
现代 Java 项目中,clone() 因设计缺陷(违反构造逻辑、与 final 字段冲突、类型强转不安全)正被逐步弃用:
-
拷贝构造器:清晰、可控、不抛异常,例如
public Person(Person original) { this.name = original.name; this.address = new Address(original.address); }; -
静态工厂方法:命名语义明确,如
Person.copyOf(person),内部可自由选择实现方式; -
Builder 模式(配合 toBuilder):Lombok 的
@Builder(toBuilder = true)生成的toBuilder().build()是浅拷贝,需手动覆盖引用字段; - 序列化反序列化(慎用):要求所有字段可序列化,性能差,仅适合配置类等低频场景。










