Java中深拷贝与浅拷贝的区别是什么?

Java 开发中对象拷贝分为浅拷贝与深拷贝两类,二者内存复制逻辑差异极大,处理引用对象时容易出现数据污染问题。本文结合完整代码实例,分别讲解浅、深拷贝底层原理、实现方式与运行效果,梳理核心区别,同时拓展 Cloneable、JSON 序列化、工具类三种主流深拷贝实现方案,清晰区分两种拷贝的适用场景。
1. 浅拷贝的原理
1.1 什么是浅拷贝?
浅拷贝 是指在拷贝对象时,复制了对象的基础数据类型的字段值,但如果对象中包含引用类型的字段(如数组、集合、对象等),那么复制的是引用类型字段的引用,而不是引用对象本身。
- 浅拷贝的特性:
- 基本数据类型会被复制一份。
- 引用类型字段会复制引用,两个对象指向相同的内存地址。
- 修改一个对象的引用类型字段,另一个对象也会受到影响。
1.2 浅拷贝的实现
代码示例:浅拷贝
class Person {
String name;
int age;
Address address; // 引用类型字段
Person(String name, int age, Address address) {
this.name = name;
this.age = age;
this.address = address;
}
@Override
public String toString() {
return "Person{name='" + name + "', age=" + age + ", address=" + address + "}";
}
}
class Address {
String city;
Address(String city) {
this.city = city;
}
@Override
public String toString() {
return city;
}
}
public class ShallowCopyExample {
public static void main(String[] args) {
Address address = new Address("New York");
Person person1 = new Person("John", 25, address);
// 浅拷贝
Person person2 = person1; // 这里是引用赋值,浅拷贝
// 修改 person2 的地址
person2.address.city = "Los Angeles";
// 输出结果
System.out.println("person1: " + person1);
System.out.println("person2: " + person2);
}
}
输出结果:
person1: Person{name='John', age=25, address=Los Angeles}
person2: Person{name='John', age=25, address=Los Angeles}
解析:
- 由于
person2 = person1是引用赋值,person1和person2实际上指向的是同一个Person对象,且其address字段指向同一个Address对象。 - 修改
person2的address.city,同时也改变了person1的address.city,这是浅拷贝的典型问题。
2. 深拷贝的原理
2.1 什么是深拷贝?
深拷贝 是指拷贝对象时,不仅复制对象本身,而且递归地复制对象的所有引用类型字段。这样,原对象和新对象将拥有完全独立的内存空间,修改一个对象的引用类型字段,不会影响另一个对象。
- 深拷贝的特性:
- 基本数据类型会被复制一份。
- 引用类型字段会被完全复制,两个对象拥有独立的内存地址。
- 修改一个对象的引用类型字段不会影响另一个对象。
2.2 深拷贝的实现
代码示例:深拷贝
class Person implements Cloneable {
String name;
int age;
Address address;
Person(String name, int age, Address address) {
this.name = name;
this.age = age;
this.address = address;
}
@Override
public Person clone() throws CloneNotSupportedException {
// 使用 Object 的 clone 方法进行浅拷贝
Person cloned = (Person) super.clone();
// 对引用类型进行深拷贝
cloned.address = new Address(this.address.city);
return cloned;
}
@Override
public String toString() {
return "Person{name='" + name + "', age=" + age + ", address=" + address + "}";
}
}
class Address {
String city;
Address(String city) {
this.city = city;
}
@Override
public String toString() {
return city;
}
}
public class DeepCopyExample {
public static void main(String[] args) throws CloneNotSupportedException {
Address address = new Address("New York");
Person person1 = new Person("John", 25, address);
// 深拷贝
Person person2 = person1.clone(); // 调用克隆方法进行深拷贝
// 修改 person2 的地址
person2.address.city = "Los Angeles";
// 输出结果
System.out.println("person1: " + person1);
System.out.println("person2: " + person2);
}
}
输出结果:
person1: Person{name='John', age=25, address=New York}
person2: Person{name='John', age=25, address=Los Angeles}
解析:
- 在深拷贝中,通过重写
clone()方法,不仅拷贝了Person对象本身,还为address字段创建了新的Address对象。因此,person1和person2完全独立。 - 修改
person2的address.city不会影响person1,这就是深拷贝的好处。
3. 浅拷贝和深拷贝的区别总结
| 对比维度 | 浅拷贝(Shallow Copy) | 深拷贝(Deep Copy) |
|---|---|---|
| 对象复制 | 只复制对象本身,引用类型的字段共享内存地址 | 不仅复制对象本身,还递归复制所有引用类型字段 |
| 内存管理 | 新旧对象共享引用类型字段的内存地址 | 新旧对象的引用类型字段各自拥有独立内存 |
| 修改影响 | 修改一个对象的引用类型字段会影响另一个对象 | 修改一个对象的引用类型字段不会影响另一个对象 |
| 性能 | 性能较高,简单实现 | 性能较低,需递归复制所有对象及其字段 |
| 实现方式 | 可以通过赋值、clone() 等方式实现 |
通常需要自定义 clone() 或使用工具库 |
知识拓展
1. Java 的 Cloneable 接口与 clone() 方法
Java 中的 Cloneable 接口允许对象支持克隆功能。Object 类提供了一个默认的 clone() 方法,但该方法默认实现的是浅拷贝,若想实现深拷贝,通常需要重写 clone() 方法,并手动处理引用类型的字段。
Cloneable接口是一个标记接口,表示对象可以被克隆。- 通过
super.clone()实现对象的浅拷贝,再自行处理引用类型字段的深拷贝。
示例:Cloneable 的使用
class Address implements Cloneable {
String city;
Address(String city) {
this.city = city;
}
@Override
public Address clone() throws CloneNotSupportedException {
return (Address) super.clone();
}
}
2. JSON 序列化与深拷贝
通过 JSON 序列化与反序列化 也可以轻松实现深拷贝。使用 Jackson 或 Gson 序列化对象后,再从 JSON 字符串恢复为新对象,从而实现深拷贝。
示例:使用 Jackson 实现深拷贝
import com.fasterxml.jackson.databind.ObjectMapper;
public class JsonDeepCopyExample {
public static void main(String[] args) throws Exception {
Person person1 = new Person("John", 25, new Address("New York"));
ObjectMapper mapper = new ObjectMapper();
// 序列化成 JSON 字符串
String json = mapper.writeValueAsString(person1);
// 从 JSON 字符串反序列化为新对象
Person person2 = mapper.readValue(json, Person.class);
// 修改 person2 的地址
person2.address.city = "Los Angeles";
// 输出
System.out.println("person1: " + person1);
System.out.println("person2: " + person2);
}
}
3. Apache Commons Lang 中的深拷贝
Apache Commons Lang 提供了一个工具类 SerializationUtils.clone(),可以通过序列化的方式实现对象的深拷贝。
示例:使用 Apache Commons Lang 进行深拷贝
import org.apache.commons.lang3.SerializationUtils;
public class ApacheCommonsDeepCopyExample {
public static void main(String[] args) {
Person person1 = new Person("John", 25, new Address("New York"));
Person person2 = SerializationUtils.clone(person1); // 直接深拷贝
person2.address.city = "Los Angeles";
System.out.println("person1: " + person1);
System.out.println("person2: " + person2);
}
}
总结
浅拷贝与深拷贝核心分界在于是否完整递归复制引用类型对象,浅拷贝性能高但数据互通,深拷贝数据完全隔离但开销更大。日常开发可根据对象结构选用对应实现方案,简单对象优先浅拷贝,多层嵌套引用对象则采用自定义克隆或序列化深拷贝,合理选择能规避数据互相篡改的线上隐患。
以上关于Java中深拷贝与浅拷贝的区别是什么?的文章就介绍到这了,更多相关内容请搜索码云笔记以前的文章或继续浏览下面的相关文章,希望大家以后多多支持码云笔记。
如若内容造成侵权/违法违规/事实不符,请将相关资料发送至 admin@mybj123.com 进行投诉反馈,一经查实,立即处理!
重要:如软件存在付费、会员、充值等,均属软件开发者或所属公司行为,与本站无关,网友需自行判断
码云笔记 » Java中深拷贝与浅拷贝的区别是什么?
微信
支付宝