跳转至

Java 中的深拷贝与浅拷贝

简介

在 Java 编程中,深拷贝(Deep Copy)和浅拷贝(Shallow Copy)是处理对象复制时的重要概念。理解它们的区别以及如何正确使用,对于编写高效、可靠的代码至关重要。本文将详细介绍 Java 中深拷贝和浅拷贝的基础概念、使用方法、常见实践以及最佳实践,帮助读者深入理解并在实际项目中合理应用。

目录

  1. 基础概念
    • 浅拷贝
    • 深拷贝
  2. 使用方法
    • 浅拷贝的实现
    • 深拷贝的实现
  3. 常见实践
    • 浅拷贝的应用场景
    • 深拷贝的应用场景
  4. 最佳实践
    • 何时选择浅拷贝
    • 何时选择深拷贝
  5. 小结
  6. 参考资料

基础概念

浅拷贝

浅拷贝是指在复制对象时,只复制对象本身和对象中基本数据类型的字段值。对于对象中的引用类型字段,它只是复制引用,而不是复制引用所指向的对象。这意味着原始对象和拷贝对象的引用类型字段指向同一个对象实例。

深拷贝

深拷贝是指在复制对象时,不仅复制对象本身和基本数据类型的字段值,还递归地复制对象中所有引用类型字段所指向的对象。这样,原始对象和拷贝对象的所有字段都有自己独立的内存空间,互不影响。

使用方法

浅拷贝的实现

在 Java 中,实现浅拷贝通常有两种方式:通过 Object 类的 clone() 方法和通过构造函数。

使用 clone() 方法

class ShallowCopyClass implements Cloneable {
    private int primitiveField;
    private AnotherClass referenceField;

    public ShallowCopyClass(int primitiveField, AnotherClass referenceField) {
        this.primitiveField = primitiveField;
        this.referenceField = referenceField;
    }

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

    // Getters and setters
    public int getPrimitiveField() {
        return primitiveField;
    }

    public void setPrimitiveField(int primitiveField) {
        this.primitiveField = primitiveField;
    }

    public AnotherClass getReferenceField() {
        return referenceField;
    }

    public void setReferenceField(AnotherClass referenceField) {
        this.referenceField = referenceField;
    }
}

class AnotherClass {
    private String data;

    public AnotherClass(String data) {
        this.data = data;
    }

    // Getters and setters
    public String getData() {
        return data;
    }

    public void setData(String data) {
        this.data = data;
    }
}

public class ShallowCopyExample {
    public static void main(String[] args) {
        AnotherClass anotherObject = new AnotherClass("Original Data");
        ShallowCopyClass originalObject = new ShallowCopyClass(10, anotherObject);

        try {
            ShallowCopyClass copiedObject = (ShallowCopyClass) originalObject.clone();

            // 修改原始对象的基本类型字段
            originalObject.setPrimitiveField(20);
            // 修改原始对象的引用类型字段
            originalObject.getReferenceField().setData("Modified Data");

            System.out.println("Original Object Primitive Field: " + originalObject.getPrimitiveField());
            System.out.println("Copied Object Primitive Field: " + copiedObject.getPrimitiveField());
            System.out.println("Original Object Reference Field Data: " + originalObject.getReferenceField().getData());
            System.out.println("Copied Object Reference Field Data: " + copiedObject.getReferenceField().getData());
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
        }
    }
}

通过构造函数实现浅拷贝

class ShallowCopyConstructor {
    private int primitiveField;
    private AnotherClass referenceField;

    public ShallowCopyConstructor(int primitiveField, AnotherClass referenceField) {
        this.primitiveField = primitiveField;
        this.referenceField = referenceField;
    }

    public ShallowCopyConstructor(ShallowCopyConstructor other) {
        this.primitiveField = other.primitiveField;
        this.referenceField = other.referenceField;
    }

    // Getters and setters
    public int getPrimitiveField() {
        return primitiveField;
    }

    public void setPrimitiveField(int primitiveField) {
        this.primitiveField = primitiveField;
    }

    public AnotherClass getReferenceField() {
        return referenceField;
    }

    public void setReferenceField(AnotherClass referenceField) {
        this.referenceField = referenceField;
    }
}

public class ShallowCopyConstructorExample {
    public static void main(String[] args) {
        AnotherClass anotherObject = new AnotherClass("Original Data");
        ShallowCopyConstructor originalObject = new ShallowCopyConstructor(10, anotherObject);

        ShallowCopyConstructor copiedObject = new ShallowCopyConstructor(originalObject);

        // 修改原始对象的基本类型字段
        originalObject.setPrimitiveField(20);
        // 修改原始对象的引用类型字段
        originalObject.getReferenceField().setData("Modified Data");

        System.out.println("Original Object Primitive Field: " + originalObject.getPrimitiveField());
        System.out.println("Copied Object Primitive Field: " + copiedObject.getPrimitiveField());
        System.out.println("Original Object Reference Field Data: " + originalObject.getReferenceField().getData());
        System.out.println("Copied Object Reference Field Data: " + copiedObject.getReferenceField().getData());
    }
}

深拷贝的实现

深拷贝的实现相对复杂,需要递归地复制所有引用类型字段。可以通过序列化和反序列化或者手动递归复制的方式实现。

通过序列化和反序列化实现深拷贝

import java.io.*;

class DeepCopyClass implements Serializable {
    private int primitiveField;
    private AnotherSerializableClass referenceField;

    public DeepCopyClass(int primitiveField, AnotherSerializableClass referenceField) {
        this.primitiveField = primitiveField;
        this.referenceField = referenceField;
    }

    public DeepCopyClass deepCopy() {
        try {
            ByteArrayOutputStream bos = new ByteArrayOutputStream();
            ObjectOutputStream oos = new ObjectOutputStream(bos);
            oos.writeObject(this);

            ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());
            ObjectInputStream ois = new ObjectInputStream(bis);
            return (DeepCopyClass) ois.readObject();
        } catch (IOException | ClassNotFoundException e) {
            e.printStackTrace();
            return null;
        }
    }

    // Getters and setters
    public int getPrimitiveField() {
        return primitiveField;
    }

    public void setPrimitiveField(int primitiveField) {
        this.primitiveField = primitiveField;
    }

    public AnotherSerializableClass getReferenceField() {
        return referenceField;
    }

    public void setReferenceField(AnotherSerializableClass referenceField) {
        this.referenceField = referenceField;
    }
}

class AnotherSerializableClass implements Serializable {
    private String data;

    public AnotherSerializableClass(String data) {
        this.data = data;
    }

    // Getters and setters
    public String getData() {
        return data;
    }

    public void setData(String data) {
        this.data = data;
    }
}

public class DeepCopyExample {
    public static void main(String[] args) {
        AnotherSerializableClass anotherObject = new AnotherSerializableClass("Original Data");
        DeepCopyClass originalObject = new DeepCopyClass(10, anotherObject);

        DeepCopyClass copiedObject = originalObject.deepCopy();

        // 修改原始对象的基本类型字段
        originalObject.setPrimitiveField(20);
        // 修改原始对象的引用类型字段
        originalObject.getReferenceField().setData("Modified Data");

        System.out.println("Original Object Primitive Field: " + originalObject.getPrimitiveField());
        System.out.println("Copied Object Primitive Field: " + copiedObject.getPrimitiveField());
        System.out.println("Original Object Reference Field Data: " + originalObject.getReferenceField().getData());
        System.out.println("Copied Object Reference Field Data: " + copiedObject.getReferenceField().getData());
    }
}

手动递归复制实现深拷贝

class ManualDeepCopyClass {
    private int primitiveField;
    private AnotherManualClass referenceField;

    public ManualDeepCopyClass(int primitiveField, AnotherManualClass referenceField) {
        this.primitiveField = primitiveField;
        this.referenceField = referenceField;
    }

    public ManualDeepCopyClass deepCopy() {
        AnotherManualClass newReferenceField = null;
        if (referenceField != null) {
            newReferenceField = referenceField.deepCopy();
        }
        return new ManualDeepCopyClass(primitiveField, newReferenceField);
    }

    // Getters and setters
    public int getPrimitiveField() {
        return primitiveField;
    }

    public void setPrimitiveField(int primitiveField) {
        this.primitiveField = primitiveField;
    }

    public AnotherManualClass getReferenceField() {
        return referenceField;
    }

    public void setReferenceField(AnotherManualClass referenceField) {
        this.referenceField = referenceField;
    }
}

class AnotherManualClass {
    private String data;

    public AnotherManualClass(String data) {
        this.data = data;
    }

    public AnotherManualClass deepCopy() {
        return new AnotherManualClass(data);
    }

    // Getters and setters
    public String getData() {
        return data;
    }

    public void setData(String data) {
        this.data = data;
    }
}

public class ManualDeepCopyExample {
    public static void main(String[] args) {
        AnotherManualClass anotherObject = new AnotherManualClass("Original Data");
        ManualDeepCopyClass originalObject = new ManualDeepCopyClass(10, anotherObject);

        ManualDeepCopyClass copiedObject = originalObject.deepCopy();

        // 修改原始对象的基本类型字段
        originalObject.setPrimitiveField(20);
        // 修改原始对象的引用类型字段
        originalObject.getReferenceField().setData("Modified Data");

        System.out.println("Original Object Primitive Field: " + originalObject.getPrimitiveField());
        System.out.println("Copied Object Primitive Field: " + copiedObject.getPrimitiveField());
        System.out.println("Original Object Reference Field Data: " + originalObject.getReferenceField().getData());
        System.out.println("Copied Object Reference Field Data: " + copiedObject.getReferenceField().getData());
    }
}

常见实践

浅拷贝的应用场景

  • 性能敏感场景:当对象结构简单,且引用类型字段的修改不会影响整体逻辑时,浅拷贝可以提高性能,因为它避免了递归复制的开销。
  • 临时数据处理:在一些临时数据处理场景中,只需要基本数据类型字段的独立副本,而引用类型字段可以共享,此时浅拷贝是合适的选择。

深拷贝的应用场景

  • 数据隔离:当需要确保原始对象和拷贝对象在任何情况下都互不影响时,深拷贝是必须的。例如,在多线程环境中,为了避免数据竞争,对共享对象进行深拷贝。
  • 复杂对象结构:对于包含多层嵌套引用类型的复杂对象结构,深拷贝可以确保所有层次的对象都有独立的副本。

最佳实践

何时选择浅拷贝

  • 对象结构简单:如果对象主要包含基本数据类型字段,且引用类型字段的共享不会导致问题,选择浅拷贝可以提高效率。
  • 性能优先:在对性能要求极高,且满足浅拷贝的条件下,优先使用浅拷贝。

何时选择深拷贝

  • 数据安全要求高:当数据的独立性和安全性至关重要,不允许原始对象和拷贝对象之间有任何数据关联时,必须选择深拷贝。
  • 复杂对象关系:对于具有复杂嵌套对象关系的场景,深拷贝可以确保对象的完整性和独立性。

小结

深拷贝和浅拷贝是 Java 编程中处理对象复制的重要概念。浅拷贝简单快速,适用于性能敏感且对象结构简单的场景;深拷贝虽然复杂且开销较大,但能保证数据的完全隔离和独立性,适用于对数据安全要求高的场景。在实际编程中,需要根据具体需求和对象结构来选择合适的拷贝方式,以实现高效、可靠的代码。

参考资料