深拷贝和浅拷贝是 Java 中对象复制的两种方式,区别在于对引用类型的处理方式不同:
1. 浅拷贝(Shallow Copy)
- 概念:只复制对象本身及其基本类型字段,对引用类型字段只复制引用地址,不会复制引用指向的实际对象。
- 特点:
- 原对象和拷贝对象共享引用类型字段的内存地址。
- 修改引用类型字段会同时影响原对象和拷贝对象。
- 实现方式:
- 调用
Object类的clone()方法(默认实现的clone()是浅拷贝)。 - 手动赋值非基本类型的字段。
- 调用
class Person implements Cloneable {
String name;
int age;
Address address;
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone(); // 浅拷贝
}
}
class Address {
String city;
}
Person p1 = new Person();
p1.name = "Alice";
p1.address = new Address();
p1.address.city = "New York";
Person p2 = (Person) p1.clone(); // 浅拷贝
p2.address.city = "London";
System.out.println(p1.address.city); // 输出 "London",共享地址。
2. 深拷贝(Deep Copy)
- 概念:复制对象本身及其所有引用类型字段指向的实际对象,创建独立的副本。
- 特点:
- 原对象与拷贝对象互不影响,彼此独立。
- 修改任何一个对象的引用字段不会影响另一个对象。
- 实现方式:
- 手动实现:递归复制每个引用字段。
- 序列化和反序列化:将对象序列化后再反序列化得到新对象。
- 第三方工具:如 Apache Commons Lang 提供的
SerializationUtils。
class Person implements Cloneable {
String name;
int age;
Address address;
@Override
protected Object clone() throws CloneNotSupportedException {
Person cloned = (Person) super.clone();
cloned.address = new Address(); // 深拷贝地址
cloned.address.city = this.address.city;
return cloned;
}
}
Person p1 = new Person();
p1.name = "Alice";
p1.address = new Address();
p1.address.city = "New York";
Person p2 = (Person) p1.clone(); // 深拷贝
p2.address.city = "London";
System.out.println(p1.address.city); // 输出 "New York",独立副本。
3. 总结
- 浅拷贝:快速但存在引用共享风险,适合轻量级对象。
- 深拷贝:更加安全但性能开销较大,适合复杂对象或引用嵌套对象。
在 JVM 中,浅拷贝和深拷贝的内存本质区别主要体现在堆内存中对象的分配和引用字段的指向上。以下从内存模型的角度来讲解这两者的差异:
1. JVM 内存模型概述
- 堆(Heap):存储对象实例和数组,所有对象都在堆内存中分配。
- 栈(Stack):存储方法的局部变量,包括原始数据类型变量和对象引用。
- 方法区(Method Area):存储类的元信息、静态变量等。
- 直接内存:与 NIO 等相关的直接分配内存。
- 程序计数器和本地方法栈:线程相关的执行信息。
拷贝操作主要涉及 栈中的引用 和 堆中的对象实例。
2. 浅拷贝的内存模型
浅拷贝过程:
- 对象的基本类型字段会复制其值。
- 引用类型字段只复制引用地址,而不是实际对象。
内存情况:
- 堆内存:只有原始对象本身被复制,引用字段仍指向相同的堆内存地址。
- 栈内存:新对象在栈中存储了新的引用,但这些引用指向的堆内存仍然是原对象的引用。
内存图示:
假设有一个类:
class Person {
String name; // 引用类型
int age; // 基本类型
Address address; // 引用类型
}
class Address {
String city;
}
浅拷贝代码:
Person p1 = new Person();
p1.name = "Alice";
p1.age = 25;
p1.address = new Address();
p1.address.city = "New York";
Person p2 = (Person) p1.clone(); // 浅拷贝
内存分布:
- p1、p2:
- 栈中存储两份
Person的引用。
- 栈中存储两份
- 堆中:
p1和p2的基本类型age独立存储。p1.address和p2.address指向同一个堆内存的Address对象。
结果:修改 p2.address.city 会影响 p1.address.city。
3. 深拷贝的内存模型
深拷贝过程:
- 对象的基本类型字段会复制其值。
- 对于引用类型字段,会递归复制其引用指向的对象,从而生成完全独立的副本。
内存情况:
- 堆内存:每个引用字段都会创建新的对象实例,所有引用都指向新分配的堆内存。
- 栈内存:新对象的引用独立存储,完全不共享堆内存地址。
内存图示:
深拷贝代码:
Person p2 = (Person) p1.clone(); // 假设 clone 方法实现了深拷贝
内存分布:
- p1、p2:
- 栈中存储两份
Person的引用。
- 栈中存储两份
- 堆中:
p1和p2的基本类型字段独立存储。p2.address指向新分配的Address对象,与p1.address完全独立。
结果:修改 p2.address.city 不会影响 p1.address.city。
4. 浅拷贝与深拷贝内存分布对比
| 特性 | 浅拷贝 | 深拷贝 |
|---|---|---|
| 基本类型字段 | 复制值 | 复制值 |
| 引用类型字段 | 复制引用地址,共享同一堆内存对象 | 递归复制引用指向的对象,堆内存中生成独立副本 |
| 栈内存分配 | 两个独立的对象引用 | 两个独立的对象引用 |
| 堆内存分配 | 原对象与拷贝对象共享部分内存 | 原对象与拷贝对象的内存完全独立 |
| 修改引用字段对原对象影响 | 会影响 | 不会影响 |
5. 适用场景与注意点
-
浅拷贝:
- 适用于性能要求高,且对象内部没有复杂引用类型的场景。
- 避免在共享引用字段修改时引发数据问题。
-
深拷贝:
- 适用于对象复杂、存在嵌套引用类型,并需要完全独立的副本时。
- 深拷贝实现成本高,可能影响性能。
6. 内存讲解总结
- 浅拷贝:栈中的引用地址复制,堆中部分内存共享。
- 深拷贝:栈中的引用地址和堆中所有内容都独立分配,从而保证数据完全隔离。

被折叠的 条评论
为什么被折叠?



