深拷贝(Deep Copy)和浅拷贝(Shallow Copy)是对象复制的两种方式,核心区别在于是否递归复制对象内部的所有引用类型成员。以下是详细对比与实现方法:
1. 核心区别
特性 | 浅拷贝 | 深拷贝 |
---|---|---|
复制范围 | 仅复制对象本身(值类型字段)和引用地址 | 递归复制对象及其所有引用类型成员的全新实例 |
内存影响 | 引用类型字段与原对象共享同一内存地址 | 所有引用类型字段均创建独立副本 |
修改影响 | 修改副本的引用类型字段会影响原对象 | 副本与原对象完全独立,互不影响 |
实现复杂度 | 简单(默认clone() 即可) | 复杂(需手动处理所有嵌套对象) |
性能开销 | 低 | 高(尤其嵌套层级深时) |
2. 代码示例(Java)
示例类定义
java
class Address implements Cloneable {
String city;
public Address(String city) { this.city = city; }
// 浅拷贝实现
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone(); // 默认浅拷贝
}
}
class Person implements Cloneable {
String name;
Address address;
public Person(String name, Address address) {
this.name = name;
this.address = address;
}
// 浅拷贝实现
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone(); // 默认浅拷贝
}
// 深拷贝实现(手动递归复制)
public Person deepCopy() throws CloneNotSupportedException {
Person copy = (Person) super.clone();
copy.address = (Address) address.clone(); // 递归复制引用类型字段
return copy;
}
}
测试代码
java
public static void main(String[] args) throws CloneNotSupportedException {
Address address = new Address("北京");
Person p1 = new Person("张三", address);
// 浅拷贝测试
Person p2Shallow = (Person) p1.clone();
p2Shallow.address.city = "上海";
System.out.println(p1.address.city); // 输出"上海"(原对象被修改)
// 深拷贝测试
Person p2Deep = p1.deepCopy();
p2Deep.address.city = "广州";
System.out.println(p1.address.city); // 输出"上海"(原对象未受影响)
}
3. 实现方式对比
浅拷贝
- 默认行为:Java的
Object.clone()
方法(需实现Cloneable
接口)。 - 适用场景:对象仅包含基本类型字段或不可变引用(如String)。
深拷贝
- 手动递归实现:逐层调用引用类型字段的
clone()
方法。 - 序列化/反序列化:通过对象流实现完全独立副本。
java
public static <T> T deepCopyBySerialization(T obj) throws IOException, ClassNotFoundException { ByteArrayOutputStream bos = new ByteArrayOutputStream(); ObjectOutputStream oos = new ObjectOutputStream(bos); oos.writeObject(obj); ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray()); ObjectInputStream ois = new ObjectInputStream(bis); return (T) ois.readObject(); }
- 工具库:使用Apache Commons Lang的
SerializationUtils.clone()
。java
Person copy = SerializationUtils.clone(p1);
4. 注意事项
- 性能问题:深拷贝可能因递归复制或序列化导致高延迟。
- 循环引用:对象内部存在循环引用时需特殊处理,避免栈溢出。
- 不可变对象:如String、Integer等无需深拷贝,直接引用即可。
- Cloneable接口:若未实现,调用
clone()
会抛出CloneNotSupportedException
。
5. 设计建议
- 优先使用深拷贝:当对象需要完全独立时(如缓存数据、线程安全场景)。
- 避免过度复制:对只读数据或不可变对象使用浅拷贝。
- 替代方案:使用Builder模式或工厂方法创建新对象,而非依赖拷贝。
总结
- 浅拷贝:复制速度快,但共享引用对象,适用于简单数据。
- 深拷贝:数据完全隔离,适用于复杂对象或需要线程安全的场景。
根据业务需求选择合适的拷贝方式,在性能与数据独立性之间取得平衡。