在Java中,深复制和浅复制是对象复制的两种重要方式,它们的主要区别在于对引用类型字段的处理。
浅复制(Shallow Copy)
浅复制只复制对象本身,而不复制对象内部的引用类型字段所指向的对象。
特点:
基本类型字段:值复制
引用类型字段:复制引用(指向同一个对象)
实现方式:
1. 使用 clone() 方法
class Person implements Cloneable {
private String name;
private int age;
private Address address; // 引用类型
// 构造方法、getter/setter省略
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone(); // 浅复制
}
}
class Address {
private String city;
private String street;
// 构造方法、getter/setter
}
2. 使用构造方法
class Person {
private String name;
private int age;
private Address address;
// 浅复制构造方法
public Person(Person other) {
this.name = other.name;
this.age = other.age;
this.address = other.address; // 共享同一个Address对象
}
}
测试浅复制:
public class ShallowCopyDemo {
public static void main(String[] args) throws CloneNotSupportedException {
Address address = new Address("北京", "长安街");
Person person1 = new Person("张三", 25, address);
Person person2 = (Person) person1.clone();
System.out.println(person1.getAddress() == person2.getAddress()); // true,同一个对象
System.out.println(person1.getName() == person2.getName()); // true,字符串常量池
// 修改原对象的引用类型字段
person1.getAddress().setCity("上海");
System.out.println(person2.getAddress().getCity()); // 输出"上海",受到影响
}
}
深复制(Deep Copy)
深复制会复制对象及其所有引用类型字段指向的对象,创建完全独立的副本。
特点:
基本类型字段:值复制
引用类型字段:创建新的对象
实现方式:
1. 重写 clone() 方法实现深复制
class Person implements Cloneable {
private String name;
private int age;
private Address address;
// 深复制
@Override
protected Object clone() throws CloneNotSupportedException {
Person cloned = (Person) super.clone();
cloned.address = (Address) address.clone(); // 复制Address对象
return cloned;
}
}
class Address implements Cloneable {
private String city;
private String street;
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
2. 使用序列化实现深复制
import java.io.*;
class DeepCopyUtil {
@SuppressWarnings("unchecked")
public static <T extends Serializable> T deepCopy(T object) {
try {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(baos);
oos.writeObject(object);
oos.close();
ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
ObjectInputStream ois = new ObjectInputStream(bais);
return (T) ois.readObject();
} catch (IOException | ClassNotFoundException e) {
throw new RuntimeException("深复制失败", e);
}
}
}
// 需要实现Serializable接口
class Person implements Serializable {
private String name;
private int age;
private Address address; // Address也需要实现Serializable
// 使用序列化进行深复制
public Person deepCopy() {
return DeepCopyUtil.deepCopy(this);
}
}
3. 使用复制构造方法实现深复制
class Person {
private String name;
private int age;
private Address address;
// 深复制构造方法
public Person(Person other) {
this.name = other.name;
this.age = other.age;
this.address = new Address(other.address); // 创建新的Address对象
}
}
class Address {
private String city;
private String street;
// 复制构造方法
public Address(Address other) {
this.city = other.city;
this.street = other.street;
}
}
测试深复制:
public class DeepCopyDemo {
public static void main(String[] args) throws CloneNotSupportedException {
Address address = new Address("北京", "长安街");
Person person1 = new Person("张三", 25, address);
Person person2 = (Person) person1.clone(); // 深复制
System.out.println(person1.getAddress() == person2.getAddress()); // false,不同对象
// 修改原对象的引用类型字段
person1.getAddress().setCity("上海");
System.out.println(person2.getAddress().getCity()); // 输出"北京",不受影响
}
}
总结对比
特性浅复制深复制
基本类型字段值复制值复制
引用类型字段复制引用创建新对象
内存占用少多
性能快慢
对象独立性不独立完全独立
实现复杂度简单复杂
选择建议
使用浅复制:当引用类型字段是不可变的,或者你确实需要共享对象时
使用深复制:当需要完全独立的副本,避免意外的副作用时
考虑性能:深复制通常更耗时,特别是在对象图很深的情况下
在实际开发中,根据具体需求选择合适的复制策略非常重要。