在 Java 中,浅拷贝(Shallow Copy)和深拷贝(Deep Copy)是两个重要概念,尤其在操作对象或数据结构时,它们决定了如何复制数据,以及数据之间是否存在关联。下面我们来详细解释它们:
1. 浅拷贝(Shallow Copy)
浅拷贝在 Java 中意味着复制一个对象时,仅复制该对象的基本字段,但如果对象内部有引用类型字段(例如数组、集合、对象),那么这些引用类型字段不会被复制,它们只是被共享。换句话说,浅拷贝只是复制对象的引用,而不是引用指向的实际对象。
在 Java 中,浅拷贝通常通过 Object
类中的 clone()
方法 实现。默认的 clone()
方法会进行浅拷贝。
浅拷贝实现:
Java 中的 clone()
方法要求类实现 Cloneable
接口,并覆盖 clone()
方法。
class Person implements Cloneable {
String name;
int age;
Address address; // 引用类型
public Person(String name, int age, Address address) {
this.name = name;
this.age = age;
this.address = address;
}
// 覆盖 clone() 方法实现浅拷贝
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone(); // 浅拷贝
}
}
class Address {
String city;
public Address(String city) {
this.city = city;
}
}
浅拷贝效果:
public class Main {
public static void main(String[] args) throws CloneNotSupportedException {
Address address = new Address("New York");
Person p1 = new Person("Alice", 30, address);
Person p2 = (Person) p1.clone(); // 浅拷贝
// 修改 p2 的地址
p2.address.city = "Los Angeles";
// p1 的地址也会改变
System.out.println(p1.address.city); // 输出 "Los Angeles"
}
}
在这个例子中,虽然我们浅拷贝了 Person
对象,但它的 address
引用仍然指向同一个对象,因此修改 p2
的 address
,p1
也会随之改变。
浅拷贝的特点:
- 基本数据类型:值被直接复制(例如
int
、boolean
等)。 - 引用类型:复制的是引用,多个对象共享同一个内存地址。
2. 深拷贝(Deep Copy)
深拷贝在 Java 中意味着不仅要复制对象的基本字段,还要递归地复制所有引用类型的字段。这意味着深拷贝将创建完全独立的副本,即使内部嵌套了复杂的对象结构,原对象和副本之间也不会共享引用。
在 Java 中,深拷贝的实现比较复杂,因为默认的 clone()
方法只进行浅拷贝,因此需要手动覆盖 clone()
方法,确保对引用类型进行递归拷贝。
深拷贝实现:
class Person implements Cloneable {
String name;
int age;
Address address; // 引用类型
public Person(String name, int age, Address address) {
this.name = name;
this.age = age;
this.address = address;
}
// 覆盖 clone() 方法实现深拷贝
@Override
protected Object clone() throws CloneNotSupportedException {
// 首先浅拷贝基本字段
Person clonedPerson = (Person) super.clone();
// 递归拷贝引用类型的字段
clonedPerson.address = (Address) this.address.clone();
return clonedPerson; // 返回完全独立的副本
}
}
class Address implements Cloneable {
String city;
public Address(String city) {
this.city = city;
}
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone(); // Address 的浅拷贝
}
}
深拷贝效果:
public class Main {
public static void main(String[] args) throws CloneNotSupportedException {
Address address = new Address("New York");
Person p1 = new Person("Alice", 30, address);
Person p2 = (Person) p1.clone(); // 深拷贝
// 修改 p2 的地址
p2.address.city = "Los Angeles";
// p1 的地址不会改变
System.out.println(p1.address.city); // 输出 "New York"
}
}
在这个例子中,p2
的 address
是独立的对象,所以修改 p2
的地址不会影响 p1
的地址。
深拷贝的特点:
- 所有字段(包括引用类型)都会被独立复制。
- 原对象与副本完全独立,任何一方的修改都不会影响另一方。
3. 浅拷贝与深拷贝的比较
对比点 | 浅拷贝 | 深拷贝 |
复制对象层次 | 只复制第一层对象,引用类型字段共享同一对象 | 递归复制所有对象及其引用类型字段 |
引用共享 | 内部引用类型共享同一内存地址 | 内部对象完全独立,不共享任何引用 |
适用场景 | 适用于简单对象,没有深层次引用 | 适用于复杂对象结构,深层对象独立 |
4. 深拷贝的其他实现方式
除了使用 clone()
方法,还可以使用其他方式实现深拷贝,比如:
- 序列化与反序列化:通过将对象序列化成字节流,再反序列化为新对象。这个方法可以保证深拷贝,但要求对象实现
Serializable
接口。 - 手动拷贝构造方法:编写一个拷贝构造函数来逐个字段地进行深拷贝。
序列化深拷贝示例:
import java.io.*;
class Person implements Serializable {
String name;
int age;
Address address;
public Person(String name, int age, Address address) {
this.name = name;
this.age = age;
this.address = address;
}
// 序列化实现深拷贝
public Person deepCopy() throws IOException, ClassNotFoundException {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(baos);
oos.writeObject(this);
ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
ObjectInputStream ois = new ObjectInputStream(bais);
return (Person) ois.readObject();
}
}
这种方法虽然可以实现深拷贝,但效率较低,不适用于高性能要求的场景。
5.总结
- 浅拷贝 复制对象的第一层,引用类型字段共享同一对象。
- 深拷贝 递归复制对象及其所有引用,确保新对象与原对象完全独立。