深拷贝与浅拷贝

在Java中,深拷贝(Deep Copy)和浅拷贝(Shallow Copy)是两种复制对象的方法,它们在处理对象及其引用类型字段时有着不同的行为。下面简述两者的区别和如何使用深拷贝复制对象。

浅拷贝

浅拷贝是创建一个新对象,这个新对象是对原对象的字段逐个拷贝。如果原对象的某个字段是一个引用类型(例如指向一个对象的引用),浅拷贝只会复制这个引用,而不会复制引用的实际对象。这样,新对象和原对象将共享相同的引用类型字段。

class Address {
    String city;
    String street;
    
    Address(String city, String street) {
        this.city = city;
        this.street = street;
    }
}

class Person implements Cloneable {
    String name;
    Address address;
    
    Person(String name, Address address) {
        this.name = name;
        this.address = address;
    }
    
    @Override
    protected Object clone() throws CloneNotSupportedException {
        return super.clone(); // 浅拷贝
    }
}

在上述代码中,Person 类实现了 Cloneable 接口,clone 方法调用了 super.clone(),这将执行浅拷贝。新 Person 对象和原对象将共享相同的 Address 对象引用。

深拷贝

深拷贝不仅创建一个新对象,还递归地复制所有引用类型的字段对象。这样,新对象和原对象完全独立,它们之间没有共享的引用。

class Address implements Cloneable {
    String city;
    String street;
    
    Address(String city, String street) {
        this.city = city;
        this.street = street;
    }
    
    @Override
    protected Object clone() throws CloneNotSupportedException {
        return (Address) super.clone();
    }
}

class Person implements Cloneable {
    String name;
    Address address;
    
    Person(String name, Address address) {
        this.name = name;
        this.address = address;
    }
    
    @Override
    protected Object clone() throws CloneNotSupportedException {
        Person clonedPerson = (Person) super.clone();
        clonedPerson.address = (Address) address.clone(); // 深拷贝地址
        return clonedPerson;
    }
}

在上述代码中,Person 类的 clone 方法不仅克隆自身,还克隆其引用类型字段 Address,实现了深拷贝。

常见的深拷贝方法

除了手动实现 clone 方法外,还有其他几种实现深拷贝的方法:

1. 使用构造函数

通过创建一个新的构造函数来复制对象的所有字段。这种方法不需要实现 Cloneable 接口,但需要手动编写构造函数。

class Address {
    String city;
    String street;
    
    Address(String city, String street) {
        this.city = city;
        this.street = street;
    }
    
    // 深拷贝构造函数
    Address(Address address) {
        this.city = address.city;
        this.street = address.street;
    }
}

class Person {
    String name;
    Address address;
    
    Person(String name, Address address) {
        this.name = name;
        this.address = address;
    }
    
    // 深拷贝构造函数
    Person(Person person) {
        this.name = person.name;
        this.address = new Address(person.address);
    }
}

2. 使用 JSON 序列化和反序列化

通过将对象转换为 JSON 字符串,然后再将其解析为对象,可以实现深拷贝。需要使用第三方库如 Gson 或 Jackson。

import com.google.gson.Gson;

class Address {
    String city;
    String street;
    
    Address(String city, String street) {
        this.city = city;
        this.street = street;
    }
}

class Person {
    String name;
    Address address;
    
    Person(String name, Address address) {
        this.name = name;
        this.address = address;
    }
}

public class Main {
    public static void main(String[] args) {
        Gson gson = new Gson();
        
        Address address = new Address("New York", "5th Avenue");
        Person person1 = new Person("John", address);
        
        // 序列化为 JSON 字符串
        String json = gson.toJson(person1);
        
        // 反序列化为新对象
        Person person2 = gson.fromJson(json, Person.class);
        
        System.out.println(person1.address == person2.address); // false
    }
}

3. 使用 Apache Commons Lang 的 SerializationUtils.clone(推荐)

这个方法非常简便,只不过需要确保所有参与深拷贝的类都实现 Serializable 接口。

import org.apache.commons.lang3.SerializationUtils;
import java.io.Serializable;

class Address implements Serializable {
    String city;
    String street;
    
    Address(String city, String street) {
        this.city = city;
        this.street = street;
    }
}

class Person implements Serializable {
    String name;
    Address address;
    
    Person(String name, Address address) {
        this.name = name;
        this.address = address;
    }
}

public class Main {
    public static void main(String[] args) {
        Address address = new Address("New York", "5th Avenue");
        Person person1 = new Person("John", address);
        Person person2 = SerializationUtils.clone(person1);
        
        System.out.println(person1.address == person2.address); // false
    }
}

总结

在Java中,深拷贝和浅拷贝是两种不同的对象复制方式。浅拷贝只复制对象的引用,而深拷贝则递归复制所有引用类型的字段。选择适当的方法来实现深拷贝取决于具体的需求和上下文。以上几种方法各有优缺点,可以根据实际情况选择使用。

posted @ 2024-06-26 11:18  Cloong  阅读(69)  评论(0)    收藏  举报