Java中实现深拷贝和浅拷贝

 Java Cloneable 接口和 Clone() 方法

  • Java提供了一个可克隆的接口,要求每个实现类都有一个clone()方法。
  • clone() 的默认性质是浅复制,因此我们需要以深复制每个引用类型而不是复制引用的方式来实现克隆。
  • 让我们看一些带有可克隆接口和clone()方法的代码示例

浅拷贝(Shallow Copy):
浅拷贝是指复制对象时,只复制对象本身及其所有基本数据类型的成员变量,而不复制引用类型的成员变量。因此,新对象和原对象中的引用类型成员变量将引用相同的对象。

在Java中,可以使用clone()方法实现浅拷贝。例如:

class Person implements Cloneable {
    String name;
    Address address;

    public Person clone() throws CloneNotSupportedException {
        return (Person) super.clone();
    }
}

class Address {
    String city;

    // constructor and other members
}

public class Main {
    public static void main(String[] args) throws CloneNotSupportedException {
        Person person1 = new Person();
        person1.name =
"John";
        person1.address = new Address();
        person1.address.city =
"New York";

        Person person2 = person1.clone();

       
// person1和person2共享同一个Address对象
        System.out.println(person1.address.city);  
// 输出 New York
        System.out.println(person2.address.city);  
// 输出 New York

       
// 修改person1的Address对象的城市属性
        person1.address.city =
"San Francisco";

       
// person1和person2都受到影响,因为它们引用同一个Address对象
        System.out.println(person1.address.city);  
// 输出 San Francisco
        System.out.println(person2.address.city);  
// 输出 San Francisco
    }
}

浅拷贝示例2

  • 用户类实现clone()方法并通过调用super.clone()返回克隆实例。
  • 基本上,它将使用值类型或引用类型克隆用户的所有属性。
  • 在浅复制中,如果属性是对象类型,则只会获取引用。例如,在 User 类中,Address 对象被复制为引用副本,而不是值副本。
  • 但是 String (因为它是不可变的)和 int (因为它是原始的)被复制为值类型。

public class User implements Cloneable{
    private String name;
    private Address address;
    private int age;
    // getters, setters
     
    @Override
    public User clone() {
        try {
            User clone = (User) super.clone();
            return clone;
        } catch (CloneNotSupportedException e) {
            throw new AssertionError();
        }
    }
}

  • 类似地,我们有一个实现clone()方法的Address类。

class Address implements Cloneable{
    private String streetName;
    private String zipCode;
    private String cityName;
    private String country;
     
    @Override
    public Address clone() {
        try {
            Address clone = (Address) super.clone();
            return clone;
        } catch (CloneNotSupportedException e) {
            throw new AssertionError();
        }
    }
}

  • 现在,如果我们测试克隆用户对象,我们将获得用户对象的浅表副本。

public static void main(String[] args) {
    System.out.println("Shallow Copy Of Address: ");
    User user =  build();;
    User userClone = user.clone();
    System.out.println(
"user object hashcode : " +user.hashCode());
    System.out.println(
"userClone object hashcode :" +userClone.hashCode());
    System.out.println(
"user address object hashcode :" +user.getAddress().hashCode());
    System.out.println(
"userClone address object hashcode: " +userClone.getAddress().hashCode());
}
 
public static User build() {
        User user = new User();
        user.setName(
"sam");
        user.setAge(10);
        Address address = new Address();
        address.setCityName(
"San Mateo");
        address.setCountry(
"USA");
        address.setZipCode(
"94402");
        address.setStreetName(
"430 Station Park Circle");
        user.setAddress(address);
        return user;
}

输出
  • 我们可以看到,用户对象的哈希码不同,但地址的哈希码相同。 
  • 这表示user.clone()仅将属性克隆为浅类型。 


深拷贝(Deep Copy):

深拷贝是指复制对象时,不仅复制对象本身及其所有基本数据类型的成员变量,还会递归复制引用类型的成员变量,使得新对象和原对象中的引用类型成员变量引用不同的对象。

实现深拷贝通常需要自定义实现,可以通过序列化(Serialization)和反序列化(Deserialization)来实现。例如:

import java.io.*;

class Person implements Serializable {
    String name;
    Address address;

    public Person deepCopy() throws IOException, ClassNotFoundException {
        // 使用序列化和反序列化实现深拷贝
        ByteArrayOutputStream bos = new ByteArrayOutputStream();
        ObjectOutputStream out = new ObjectOutputStream(bos);
        out.writeObject(this);

        ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());
        ObjectInputStream in = new ObjectInputStream(bis);
        return (Person) in.readObject();
    }
}

class Address implements Serializable {
    String city;

   
// constructor and other members
}

public class Main {
    public static void main(String[] args) throws IOException, ClassNotFoundException {
        Person person1 = new Person();
        person1.name =
"John";
        person1.address = new Address();
        person1.address.city =
"New York";

        Person person2 = person1.deepCopy();

       
// person1和person2拥有各自独立的Address对象
        System.out.println(person1.address.city);  
// 输出 New York
        System.out.println(person2.address.city);  
// 输出 New York

       
// 修改person1的Address对象的城市属性
        person1.address.city =
"San Francisco";

       
// person2不受影响,因为它拥有一个独立的Address对象
        System.out.println(person1.address.city);  
// 输出 San Francisco
        System.out.println(person2.address.city);  
// 输出 New York
    }
}

深拷贝示例2

  • 在下面的示例中,我们像往常一样实现clone()方法,但主要区别在于我们还克隆地址,以便克隆用户返回对象的深拷贝,而不是地址的浅拷贝。

public class User implements Cloneable{
private String name;
    private Address address;
    private int age;
     
   // getters , setters
       @Override
    public User clone() {
        try {
            User user = (User) super.clone();
            user.setAddress(user.getAddress().clone());
// deep copy of reference
            return user;
        } catch (CloneNotSupportedException e) {
            throw new AssertionError();
        }
    }
}

  • 地址类 Clone() 实现不会改变,因为它没有自定义数据类型,并且 String 是不可变的,因此它不会返回浅引用。

class Address implements Cloneable{
    private String streetName;
    private String zipCode;
    private String cityName;
    private String country;
   // getters, setters
   @Override
    public Address clone() {
        try {
            Address clone = (Address) super.clone();
            return clone;
        } catch (CloneNotSupportedException e) {
            throw new AssertionError();
        }
    }

  • 现在如果我们测试,这次我们将得到一个深度克隆的用户对象。

public static void main(String[] args) {
    System.out.println("Deep Copy Of Address: ");
    User user =  build();;
    User userClone = user.clone();
    System.out.println(
"user object hashcode : " +user.hashCode());
    System.out.println(
"userClone object hashcode :" +userClone.hashCode());
    System.out.println(
"user address object hashcode :" +user.getAddress().hashCode());
    System.out.println(
"userClone address object hashcode: " +userClone.getAddress().hashCode());
}
 
public static User build() {
    User user = new User();
    user.setName(
"sam");
    user.setAge(10);
    Address address = new Address();
    address.setCityName(
"San Mateo");
    address.setCountry(
"USa");
    address.setZipCode(
"94402");
    address.setStreetName(
"430 Station Park Circle");
    user.setAddress(address);
    return user;

输出:

  • 我们可以看到用户对象及其克隆的哈希码是不同的,因此地址的哈希码是不同的。