Java中的深拷贝与浅拷贝
在Java中,深拷贝和浅拷贝是两种不同的对象复制方式,主要区别在于它们如何处理对象内部的引用类型字段。
浅拷贝(Shallow Copy)
浅拷贝创建一个新对象,并将原对象的字段值复制到新对象中。如果字段是基本类型,则直接复制值;如果字段是引用类型,则复制引用(即内存地址),因此新对象和原对象共享相同的引用类型字段。
实现方式:
- 实现 Cloneable 接口并重写 clone() 方法。
 - 使用工具类(如 BeanUtils.copyProperties)。
 
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(); // 浅拷贝
    }
}
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("Beijing");
        Person person1 = new Person("Alice", address);
        Person person2 = (Person) person1.clone();
        System.out.println(person1.address == person2.address); // true,共享同一个Address对象
    }
}
 
深拷贝(Deep Copy)
深拷贝创建一个新对象,并递归地复制原对象的所有字段。如果字段是引用类型,则创建该字段的新副本,而不是共享引用。因此,新对象和原对象完全独立。
 实现方式:
- 手动实现递归复制。
 - 使用序列化(如 Serializable 接口)。
 - 使用第三方库(如 Apache Commons Lang 的 SerializationUtils)。
 
- 如果对象包含循环引用,深拷贝需要特别处理,否则可能导致栈溢出。
 - 序列化方式实现深拷贝要求所有相关类都实现 Serializable 接口。
 
import java.io.*;
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();
    }
}
class Address implements Serializable {
    String city;
    public Address(String city) {
        this.city = city;
    }
}
public class Main {
    public static void main(String[] args) throws IOException, ClassNotFoundException {
        Address address = new Address("Beijing");
        Person person1 = new Person("Alice", address);
        Person person2 = person1.deepCopy();
        System.out.println(person1.address == person2.address); // false,Address对象是独立的
    }
}
 
对比
| 特性 | 浅拷贝 | 深拷贝 | 
|---|---|---|
| 基本类型字段 | 复制值 | 复制值 | 
| 引用类型字段 | 复制引用(共享同一对象) | 递归复制(创建新对象) | 
| 实现复杂度 | 简单 | 复杂 | 
| 性能 | 较高 | 较低 | 
| 适用场景 | 对象内部没有引用类型字段或共享引用无影响 | 需要完全独立的对象副本 | 
拓展知识
Java 中的 clone() 方法
- clone() 是 Object 类的一个方法,用于创建对象的副本。
 - 默认的 clone() 方法是浅拷贝。
 - 使用 clone() 需要满足以下条件: 
  
- 类必须实现 Cloneable 接口(标记接口,没有方法)。
 - 重写 clone() 方法,并将其访问修饰符改为 public。
 
 - 注意:clone() 方法不会调用构造函数。
 
class Person implements Cloneable {
    String name;
    int age;
    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }
    @Override
    public Person clone() {
        try {
            return (Person) super.clone();
        } catch (CloneNotSupportedException e) {
            throw new RuntimeException(e);
        }
    }
}
 
序列化实现深拷贝
通过序列化和反序列化可以实现深拷贝。但要求所有相关类都实现 Serializable 接口。
- 优点:简单易用,适合复杂对象图的深拷贝。
 - 缺点:性能较低,且要求所有字段都可序列化。
 
import java.io.*;
class DeepCopyUtil {
    public static <T extends Serializable> T deepCopy(T object) {
        try (ByteArrayOutputStream bos = new ByteArrayOutputStream();
             ObjectOutputStream oos = new ObjectOutputStream(bos)) {
            oos.writeObject(object);
            try (ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());
                 ObjectInputStream ois = new ObjectInputStream(bis)) {
                return (T) ois.readObject();
            }
        } catch (IOException | ClassNotFoundException e) {
            throw new RuntimeException("Deep copy failed", e);
        }
    }
}
 
深拷贝与不可变对象
 如果对象是不可变的(如 String、Integer 等),则浅拷贝和深拷贝的效果相同。不可变对象的值无法被修改,因此共享引用是安全的。
String s1 = "Hello";
String s2 = s1; // 浅拷贝,但因为是不可变对象,所以安全
 
深拷贝的性能问题
 深拷贝需要递归复制整个对象图,可能会消耗较多内存和 CPU 资源。如果对象图非常大或嵌套层级很深,深拷贝可能会导致性能问题。
 优化方法:
- 使用懒加载(Lazy Copy):只有在修改时才复制对象。
 - 使用对象池或缓存机制。
 
深拷贝与线程安全
如果多个线程共享同一个对象,浅拷贝可能导致线程安全问题。深拷贝可以避免线程安全问题,因为每个线程操作的是独立的对象副本。
