在Java中,浅克隆(Shallow Clone)和深克隆(Deep Clone)是两种不同的对象复制方式,主要区别在于对引用类型字段的处理:
一、核心区别
特性 | 浅克隆 | 深克隆 |
---|---|---|
引用类型字段 | 复制引用地址(原对象和克隆对象共享引用对象) | 递归复制整个对象图(创建完全独立的副本) |
内存关系 | 引用类型字段指向同一内存地址 | 引用类型字段指向不同的内存地址 |
数据安全性 | 修改引用对象会影响原对象 | 修改引用对象不影响原对象 |
实现复杂度 | 简单 | 复杂 |
性能开销 | 低 | 高 |
二、实现方式
1. 浅克隆实现步骤
java
// 1. 实现Cloneable接口(标记接口)
class Person implements Cloneable {
private String name;
private Address address; // 引用类型字段
@Override
public Object clone() throws CloneNotSupportedException {
// 2. 调用Object.clone()实现浅克隆
return super.clone(); // 仅复制基本类型和引用地址
}
}
// 测试
Person original = new Person("Tom", new Address("北京"));
Person cloned = (Person) original.clone();
// 修改克隆对象的引用类型字段会影响原对象
cloned.getAddress().setCity("上海");
System.out.println(original.getAddress().getCity()); // 输出"上海"(共享对象)
2. 深克隆实现方式
(1) 递归重写clone()
方法
java
class Address implements Cloneable {
private String city;
@Override
public Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
class Person implements Cloneable {
private String name;
private Address address;
@Override
public Object clone() throws CloneNotSupportedException {
Person cloned = (Person) super.clone();
cloned.address = (Address) this.address.clone(); // 递归克隆引用对象
return cloned;
}
}
// 测试
Person original = new Person("Tom", new Address("北京"));
Person cloned = (Person) original.clone();
cloned.getAddress().setCity("上海");
System.out.println(original.getAddress().getCity()); // 输出"北京"(独立对象)
(2) 通过序列化实现(推荐)
java
import java.io.*;
class Person implements Serializable {
private String name;
private Address address; // Address也需实现Serializable
public Person deepClone() {
try (ByteArrayOutputStream bos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(bos)) {
oos.writeObject(this); // 序列化对象
try (ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());
ObjectInputStream ois = new ObjectInputStream(bis)) {
return (Person) ois.readObject(); // 反序列化生成新对象
}
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
// 测试
Person cloned = original.deepClone();
三、关键注意事项
Cloneable接口:
- 是标记接口(无方法),不实现会抛
CloneNotSupportedException
- 必须重写
Object.clone()
(原生方法是protected)
- 是标记接口(无方法),不实现会抛
深克隆的挑战:
- 需要递归处理所有引用类型字段
- 循环引用可能导致栈溢出(需特殊处理)
- 序列化方式要求所有对象实现
Serializable
最佳实践:
java// 在深克隆中处理null引用避免NPE if (this.address != null) { cloned.address = (Address) this.address.clone(); }
替代方案:
- 使用第三方库(如Apache Commons Lang的
SerializationUtils.clone()
) - 手动复制构造器(推荐):java
public Person(Person source) { this.name = source.name; this.address = new Address(source.address); // 深拷贝 }
- 使用第三方库(如Apache Commons Lang的
四、总结
场景 | 推荐方式 |
---|---|
无引用类型字段 | 浅克隆(简单高效) |
嵌套引用对象 | 深克隆(数据安全) |
复杂对象图 | 序列化深克隆 |
高性能要求 | 手动实现复制构造器 |
📌 设计建议:优先考虑使用复制构造器或工厂方法实现深拷贝,避免
clone()
方法的复杂性。Java的clone()
机制存在设计缺陷(详见《Effective Java》第13条),许多现代框架已弃用该方式。