深克隆和浅克隆的区别?
参考回答
浅克隆和深克隆是 Java 对象复制的两种方式,主要区别在于是否复制对象的引用类型字段:
- 浅克隆:
- 只复制对象的基本数据类型和引用类型的引用地址,不会复制引用类型所指向的对象。
- 被克隆对象与原对象的引用字段指向同一块内存。
- 通过实现
Cloneable
接口并重写clone()
方法实现。
- 深克隆:
- 复制对象的基本数据类型,并递归地复制引用类型所指向的对象(即引用字段所指的对象也会被克隆)。
- 被克隆对象与原对象完全独立,互不影响。
- 通常通过序列化与反序列化、或递归调用
clone()
方法实现。
详细讲解与拓展
1. 浅克隆的原理与示例
浅克隆会将对象的基本数据类型字段复制一份,但对于引用类型字段,只会复制它们的引用地址,而不会复制引用指向的对象。
示例:
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 class ShallowCloneExample {
public static void main(String[] args) throws CloneNotSupportedException {
Address address = new Address("New York");
Person person1 = new Person("Alice", address);
// 浅克隆
Person person2 = (Person) person1.clone();
System.out.println("Before modification:");
System.out.println("person1 address: " + person1.address.city);
System.out.println("person2 address: " + person2.address.city);
// 修改克隆对象的引用字段
person2.address.city = "Los Angeles";
System.out.println("\nAfter modification:");
System.out.println("person1 address: " + person1.address.city);
System.out.println("person2 address: " + person2.address.city);
}
}
输出:
Before modification:
person1 address: New York
person2 address: New York
After modification:
person1 address: Los Angeles
person2 address: Los Angeles
解释:
- 浅克隆时,
person2.address
和person1.address
指向同一个对象。 - 修改
person2.address.city
会影响到person1.address.city
。
2. 深克隆的原理与示例
深克隆会递归地复制对象的所有字段,包括引用类型字段所指向的对象,使克隆对象和原对象完全独立。
方法 1:通过递归调用 clone()
实现深克隆
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 {
// 深克隆
Person cloned = (Person) super.clone();
cloned.address = (Address) address.clone(); // 克隆引用字段
return cloned;
}
}
public class DeepCloneExample {
public static void main(String[] args) throws CloneNotSupportedException {
Address address = new Address("New York");
Person person1 = new Person("Alice", address);
// 深克隆
Person person2 = (Person) person1.clone();
System.out.println("Before modification:");
System.out.println("person1 address: " + person1.address.city);
System.out.println("person2 address: " + person2.address.city);
// 修改克隆对象的引用字段
person2.address.city = "Los Angeles";
System.out.println("\nAfter modification:");
System.out.println("person1 address: " + person1.address.city);
System.out.println("person2 address: " + person2.address.city);
}
}
输出:
Before modification:
person1 address: New York
person2 address: New York
After modification:
person1 address: New York
person2 address: Los Angeles
解释:
- 深克隆时,
person2.address
是一个新的对象,与person1.address
相互独立。 - 修改
person2.address.city
不会影响person1.address.city
。
方法 2:通过序列化实现深克隆
使用序列化(Serializable
接口)和反序列化,自动实现深克隆。
import java.io.*;
class Address implements Serializable {
String city;
public Address(String city) {
this.city = city;
}
}
class Person implements Serializable {
String name;
Address address;
public Person(String name, Address address) {
this.name = name;
this.address = address;
}
}
public class DeepCloneWithSerialization {
public static void main(String[] args) throws IOException, ClassNotFoundException {
Address address = new Address("New York");
Person person1 = new Person("Alice", address);
// 序列化与反序列化实现深克隆
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(baos);
oos.writeObject(person1);
ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
ObjectInputStream ois = new ObjectInputStream(bais);
Person person2 = (Person) ois.readObject();
System.out.println("Before modification:");
System.out.println("person1 address: " + person1.address.city);
System.out.println("person2 address: " + person2.address.city);
// 修改克隆对象的引用字段
person2.address.city = "Los Angeles";
System.out.println("\nAfter modification:");
System.out.println("person1 address: " + person1.address.city);
System.out.println("person2 address: " + person2.address.city);
}
}
输出:
Before modification:
person1 address: New York
person2 address: New York
After modification:
person1 address: New York
person2 address: Los Angeles
解释:
- 序列化和反序列化的过程会创建一个完全独立的对象,自动实现深克隆。
3. 浅克隆与深克隆的区别总结
特性 | 浅克隆 | 深克隆 |
---|---|---|
复制内容 | 只复制基本数据类型和引用类型的地址 | 递归地复制所有字段,包括引用类型所指的对象 |
引用类型字段 | 原对象与克隆对象共享引用类型字段 | 原对象与克隆对象的引用字段互相独立 |
实现复杂度 | 简单,通过 super.clone() 实现 |
较复杂,需要递归 clone() 或序列化 |
使用场景 | 对象结构简单,不涉及深层嵌套引用的情况 | 对象结构复杂,包含深层嵌套引用的情况 |
4. 拓展知识
Cloneable
接口的限制Cloneable
接口只是一个标记接口,没有定义方法。- 如果不重写
clone()
方法,会抛出CloneNotSupportedException
。
- 替代方案
- 在大多数实际开发中,
Cloneable
的使用较少,因为其不够灵活且易出错。 - 常用的替代方案是:
- 构造函数:通过手动编写复制构造函数创建新对象。
- 序列化:用序列化和反序列化实现深克隆。
- 在大多数实际开发中,
- 性能对比
- 浅克隆的性能较好,因为只需复制字段引用。
- 深克隆的性能相对较低,尤其是通过序列化实现时,需要大量 I/O 操作。
5. 总结
- 浅克隆:
- 简单快捷,但无法复制引用类型指向的对象。
- 使用场景:对象结构简单或对引用字段的共享没有影响。
- 深克隆:
- 完全独立的复制,适用于复杂嵌套对象。
- 实现方式:递归调用
clone()
方法或使用序列化。