在 Java 中,对象的拷贝分为浅拷贝和深拷贝,下面分别介绍它们的实现方式以及两者的区别。
浅拷贝和深拷贝的区别
- 浅拷贝(Shallow Copy):浅拷贝创建一个新对象,新对象的属性值与原对象相同。对于基本数据类型,会直接复制其值;而对于引用数据类型,复制的是引用,即新对象和原对象的引用数据类型属性指向同一个内存地址。因此,当修改其中一个对象的引用数据类型属性时,另一个对象的该属性也会受到影响。
- 深拷贝(Deep Copy):深拷贝同样创建一个新对象,新对象的属性值与原对象相同。但对于引用数据类型,会递归地复制其对象,即新对象和原对象的引用数据类型属性指向不同的内存地址。这样,修改其中一个对象的引用数据类型属性不会影响另一个对象。
实现方式
浅拷贝的实现
在 Java 中,要实现浅拷贝,可以让类实现 Cloneable
接口,并重写 clone()
方法。Cloneable
接口是一个标记接口,用于表示该类可以被克隆。
class Address {
String street;
public Address(String street) {
this.street = street;
}
}
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 class ShallowCopyExample {
public static void main(String[] args) throws CloneNotSupportedException {
Address address = new Address("123 Main St");
Person person1 = new Person("John", address);
// 进行浅拷贝
Person person2 = (Person) person1.clone();
// 修改 person2 的地址属性
person2.address.street = "456 Elm St";
// 输出 person1 的地址,发现也被修改了
System.out.println(person1.address.street);
}
}
在上述代码中,Person
类实现了 Cloneable
接口并重写了 clone()
方法。当调用 person1.clone()
进行浅拷贝时,person2
的 address
属性和 person1
的 address
属性指向同一个 Address
对象,因此修改 person2
的 address
属性会影响 person1
的 address
属性。
深拷贝的实现
方式一:手动实现
手动实现深拷贝需要递归地复制引用数据类型的属性。
class Address implements Cloneable {
String street;
public Address(String street) {
this.street = street;
}
@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 {
Person cloned = (Person) super.clone();
// 手动复制引用数据类型属性
cloned.address = (Address) address.clone();
return cloned;
}
}
public class DeepCopyExample {
public static void main(String[] args) throws CloneNotSupportedException {
Address address = new Address("123 Main St");
Person person1 = new Person("John", address);
// 进行深拷贝
Person person2 = (Person) person1.clone();
// 修改 person2 的地址属性
person2.address.street = "456 Elm St";
// 输出 person1 的地址,发现未被修改
System.out.println(person1.address.street);
}
}
在上述代码中,Person
类和 Address
类都实现了 Cloneable
接口并重写了 clone()
方法。在 Person
类的 clone()
方法中,不仅调用了 super.clone()
复制基本数据类型属性,还手动复制了引用数据类型属性 address
,从而实现了深拷贝。
方式二:使用序列化和反序列化
通过将对象序列化为字节流,再将字节流反序列化为新对象,也可以实现深拷贝。这种方式要求对象及其引用的所有对象都实现 Serializable
接口。
import java.io.*;
class Address implements Serializable {
String street;
public Address(String street) {
this.street = street;
}
}
class Person implements Serializable {
String name;
Address address;
public Person(String name, Address address) {
this.name = name;
this.address = address;
}
public Person deepCopy() throws IOException, ClassNotFoundException {
ByteArrayOutputStream bos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(bos);
oos.writeObject(this);
ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());
ObjectInputStream ois = new ObjectInputStream(bis);
return (Person) ois.readObject();
}
}
public class DeepCopyWithSerializationExample {
public static void main(String[] args) throws IOException, ClassNotFoundException {
Address address = new Address("123 Main St");
Person person1 = new Person("John", address);
// 进行深拷贝
Person person2 = person1.deepCopy();
// 修改 person2 的地址属性
person2.address.street = "456 Elm St";
// 输出 person1 的地址,发现未被修改
System.out.println(person1.address.street);
}
}
在上述代码中,Person
类和 Address
类都实现了 Serializable
接口。Person
类的 deepCopy()
方法通过将对象序列化为字节流,再反序列化为新对象,实现了深拷贝。
综上所述,浅拷贝只复制对象的基本数据类型和引用,而深拷贝会递归地复制对象及其引用的所有对象,确保新对象和原对象相互独立。