JAVA 中的浅拷贝与深拷贝

43 阅读3分钟

在 JAVA 编程中,对象的复制是一种基本操作。然而,不同类型的对象复制可能导致不同的结果。

1、什么是浅拷贝

浅拷贝是指在复制对象时,只复制对象本身和其非引用类型的成员变量,而不复制引用类型的成员变量。

这意味着新对象和原对象的引用类型成员变量仍然指向相同的对象。

在Java中,常见的浅拷贝方式包括对象的 clone() 方法和一些库中提供的工具类,如 ObjectUtils.clone()

public class Person implements Cloneable {
    private String name;
    private Address address;

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

    // Getters and setters...

    @Override
    public Object clone() throws CloneNotSupportedException {
        return super.clone();
    }
}

public class Address {
    private String city;
    private String zipCode;

    // Getters and setters...
}

// 在使用中
Address originalAddress = new Address("City", "12345");
Person originalPerson = new Person("John", originalAddress);

try {
    // 浅拷贝
    Person clonedPerson = (Person) originalPerson.clone();

    // 修改原对象的引用类型成员变量
    originalAddress.setCity("New City");

    // 输出新对象的引用类型成员变量
    System.out.println(clonedPerson.getAddress().getCity()); // 输出 "New City"
} catch (CloneNotSupportedException e) {
    e.printStackTrace();
}

在上述示例中,originalPersonclonedPerson 共享同一个 Address 对象,因此对 originalAddress 的修改也会影响到 clonedPerson

2、什么是深拷贝

深拷贝是指在复制对象时,不仅复制对象本身和其非引用类型的成员变量,还对引用类型的成员变量进行递归复制,生成新的对象。

这样,新对象和原对象的引用类型成员变量将分别指向不同的对象。

在Java中,实现深拷贝的方式主要有手动编写深拷贝方法、使用序列化和反序列化,以及一些第三方库的支持。

import java.io.*;

public class DeepCloneUtil {
    @SuppressWarnings("unchecked")
    public static <T extends Serializable> T deepClone(T obj) {
        try (ByteArrayOutputStream bos = new ByteArrayOutputStream();
             ObjectOutputStream oos = new ObjectOutputStream(bos)) {
            // 将对象写入字节数组输出流
            oos.writeObject(obj);
            oos.flush();

            // 从字节数组输出流读取对象
            try (ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(bos.toByteArray()))) {
                return (T) ois.readObject();
            }
        } catch (IOException | ClassNotFoundException e) {
            e.printStackTrace();
            return null;
        }
    }
}

// 在使用中
Address originalAddress = new Address("City", "12345");
Person originalPerson = new Person("John", originalAddress);

// 深拷贝
Person clonedPerson = DeepCloneUtil.deepClone(originalPerson);

// 修改原对象的引用类型成员变量
originalAddress.setCity("New City");

// 输出新对象的引用类型成员变量
System.out.println(clonedPerson.getAddress().getCity()); // 输出 "City"

在上述示例中,clonedPersonAddress 对象与 originalPersonAddress 对象是独立的,修改 originalAddress 不会影响到 clonedPerson

3、如何选择浅拷贝和深拷贝

选择浅拷贝还是深拷贝取决于具体的业务需求和对象结构。在一些场景中,浅拷贝已经足够满足要求,而在另一些场景中,需要确保复制对象的所有成员变量都是独立的,这时候深拷贝更为合适。

浅拷贝的应用场景

  • 对象内部的引用类型成员变量本身就不需要被复制,或者共享同一个实例对程序逻辑无影响。
  • 复制对象的目的主要是为了创建一个对象的副本,但不关心其内部引用类型成员变量的变化。

深拷贝的应用场景

  • 需要确保复制对象的所有成员变量都是独立的,修改其中一个对象的成员变量不会影响到其他对象。
  • 复制对象的目的主要是为了创建一个完全独立的副本,任何对副本的修改都不应该影响到原对象。

在Java中,深入理解浅拷贝和深拷贝是编写高质量、高性能代码的关键。

通过灵活选择适当的拷贝方式,可以确保对象在复制过程中得到正确的处理,同时避免引起潜在的bug。

在实际项目中,根据具体需求和场景选择适当的拷贝方式,将有助于提高代码的可维护性和性能。