在 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() 进行浅拷贝时,person2address 属性和 person1address 属性指向同一个 Address 对象,因此修改 person2address 属性会影响 person1address 属性。

深拷贝的实现

方式一:手动实现

手动实现深拷贝需要递归地复制引用数据类型的属性。

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() 方法通过将对象序列化为字节流,再反序列化为新对象,实现了深拷贝。

综上所述,浅拷贝只复制对象的基本数据类型和引用,而深拷贝会递归地复制对象及其引用的所有对象,确保新对象和原对象相互独立。