【Java基础】深入解析 Java 的深拷贝与浅拷贝:原理、实现与开源框架应用

Scroll Down

深入解析 Java 的深拷贝与浅拷贝:原理、实现与开源框架应用

在 Java 开发中,我们经常需要复制对象,但不同的复制方式会导致截然不同的行为。浅拷贝(Shallow Copy)深拷贝(Deep Copy) 是两种常见的对象克隆方式,它们在不同场景下有各自的优缺点。

本篇文章将深入解析深拷贝与浅拷贝的原理,探讨如何在 Java 中实现它们,并分析它们在 JDK 和 Spring 等开源框架中的实际应用。


📌 1. 什么是浅拷贝与深拷贝?

✅ 浅拷贝(Shallow Copy)

  • 仅复制对象本身的 基本数据类型字段(如 intdouble)。
  • 对于 引用类型字段(如 ListMap、自定义对象),仅复制其内存地址,使其指向相同的对象。

📌 影响: 浅拷贝后的对象共享同一个引用字段,修改其中一个对象的引用字段,另一个对象也会受影响。

✅ 深拷贝(Deep Copy)

  • 复制对象本身的 基本数据类型字段
  • 对于 引用类型字段,不只是复制内存地址,而是创建一个新的对象,并复制其内容,从而实现真正的独立。

📌 影响: 深拷贝后的对象完全独立,修改其中一个不会影响另一个。


📌 2. 代码实战:浅拷贝 vs 深拷贝

我们用 Person 类作为示例,它包含一个 Address 对象,来对比浅拷贝和深拷贝的不同。

🎯 1️⃣ 浅拷贝示例

class Address {
    String city;

    public Address(String city) {
        this.city = city;
    }
}

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;
    }

    @Override
    protected Object clone() throws CloneNotSupportedException {
        return super.clone(); // 浅拷贝
    }
}

🌟 测试代码:

public class ShallowCopyDemo {
    public static void main(String[] args) throws CloneNotSupportedException {
        Address address = new Address("Beijing");
        Person p1 = new Person("Alice", 25, address);
        Person p2 = (Person) p1.clone(); // 浅拷贝

        System.out.println("Before modification:");
        System.out.println("p1 address: " + p1.address.city);
        System.out.println("p2 address: " + p2.address.city);

        p2.address.city = "Shanghai"; // 修改 p2 的地址

        System.out.println("After modification:");
        System.out.println("p1 address: " + p1.address.city);
        System.out.println("p2 address: " + p2.address.city);
    }
}

🚀 运行结果:

Before modification:
p1 address: Beijing
p2 address: Beijing

After modification:
p1 address: Shanghai
p2 address: Shanghai

📌 说明:

  • 由于 p1p2 共享同一个 Address 对象,所以修改 p2address.cityp1address.city 也发生了变化。
  • 这就是浅拷贝的问题,对象之间可能会产生意外的联动。

🎯 2️⃣ 深拷贝示例

我们对 clone() 方法进行修改,使 Address 也实现 Cloneable,以实现真正的深拷贝:

class Address implements Cloneable {
    String city;

    public Address(String city) {
        this.city = city;
    }

    @Override
    protected Object clone() throws CloneNotSupportedException {
        return super.clone(); // 深拷贝 Address
    }
}

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;
    }

    @Override
    protected Object clone() throws CloneNotSupportedException {
        Person cloned = (Person) super.clone();
        cloned.address = (Address) address.clone(); // 深拷贝 Address
        return cloned;
    }
}

🌟 测试代码:

public class DeepCopyDemo {
    public static void main(String[] args) throws CloneNotSupportedException {
        Address address = new Address("Beijing");
        Person p1 = new Person("Alice", 25, address);
        Person p2 = (Person) p1.clone(); // 深拷贝

        System.out.println("Before modification:");
        System.out.println("p1 address: " + p1.address.city);
        System.out.println("p2 address: " + p2.address.city);

        p2.address.city = "Shanghai"; // 修改 p2 的地址

        System.out.println("After modification:");
        System.out.println("p1 address: " + p1.address.city);
        System.out.println("p2 address: " + p2.address.city);
    }
}

🚀 运行结果:

Before modification:
p1 address: Beijing
p2 address: Beijing

After modification:
p1 address: Beijing
p2 address: Shanghai

📌 说明:

  • 由于 clone() 方法对 Address 也进行了克隆,所以 p1p2 不再共享 Address 对象,修改 p2address.city 不会影响 p1

📌 3. Java 开源框架中的深拷贝与浅拷贝

✅ JDK 中的应用

  1. Object.clone()Cloneable 接口允许对象执行浅拷贝,但不会自动实现深拷贝。
  2. ArrayList.clone()ArrayListclone() 方法是浅拷贝,元素对象不会被克隆。
  3. Serializable 机制:通过 序列化 & 反序列化 可以实现深拷贝。

✅ Spring 框架中的应用

  1. Spring Bean 的 Scope("prototype")

    @Bean
    @Scope("prototype")
    public User user() {
        return new User("Alice", 25);
    }
    

    Spring 的 prototype 作用域类似于深拷贝,每次获取 Bean 都会返回新的实例。

  2. Spring 的 BeanUtils.cloneBean()

    User user2 = (User) BeanUtils.cloneBean(user1);
    

    BeanUtils.cloneBean() 也可以实现 Java 对象的浅拷贝。


📌 4. 总结

  • 浅拷贝 仅复制对象本身,而不复制其引用对象,容易导致对象共享数据的问题。
  • 深拷贝 需要手动克隆引用对象,保证新对象互不影响。
  • JDK 提供 clone() 方法实现浅拷贝,序列化可以实现深拷贝
  • Spring 中的 prototype 作用域和 BeanUtils.cloneBean() 也涉及对象克隆

💡 选择哪种方式,取决于实际需求! 🚀