浅拷贝与深拷贝对象

JavaJavaBeginner
立即练习

💡 本教程由 AI 辅助翻译自英文原版。如需查看原文,您可以 切换至英文原版

介绍

浅拷贝(shallow copy)只会简单地将原始对象的字段值复制到新对象中。在浅拷贝中,如果原始对象包含对其他类对象的引用,那么只会复制该对象的引用。因此,对该引用对象的任何更改都会反映在原始对象和克隆对象中。然而,深拷贝(deep copy)会创建一个新对象,包括所有引用的对象,并且对克隆对象的任何更改都不会影响原始对象。


Skills Graph

%%%%{init: {'theme':'neutral'}}%%%% flowchart RL java(("`Java`")) -.-> java/ProgrammingTechniquesGroup(["`Programming Techniques`"]) java(("`Java`")) -.-> java/ObjectOrientedandAdvancedConceptsGroup(["`Object-Oriented and Advanced Concepts`"]) java(("`Java`")) -.-> java/FileandIOManagementGroup(["`File and I/O Management`"]) java(("`Java`")) -.-> java/SystemandDataProcessingGroup(["`System and Data Processing`"]) java/ProgrammingTechniquesGroup -.-> java/method_overriding("`Method Overriding`") java/ObjectOrientedandAdvancedConceptsGroup -.-> java/classes_objects("`Classes/Objects`") java/ObjectOrientedandAdvancedConceptsGroup -.-> java/constructors("`Constructors`") java/ObjectOrientedandAdvancedConceptsGroup -.-> java/encapsulation("`Encapsulation`") java/ObjectOrientedandAdvancedConceptsGroup -.-> java/interface("`Interface`") java/ObjectOrientedandAdvancedConceptsGroup -.-> java/serialization("`Serialization`") java/FileandIOManagementGroup -.-> java/io("`IO`") java/SystemandDataProcessingGroup -.-> java/object_methods("`Object Methods`") subgraph Lab Skills java/method_overriding -.-> lab-117441{{"`浅拷贝与深拷贝对象`"}} java/classes_objects -.-> lab-117441{{"`浅拷贝与深拷贝对象`"}} java/constructors -.-> lab-117441{{"`浅拷贝与深拷贝对象`"}} java/encapsulation -.-> lab-117441{{"`浅拷贝与深拷贝对象`"}} java/interface -.-> lab-117441{{"`浅拷贝与深拷贝对象`"}} java/serialization -.-> lab-117441{{"`浅拷贝与深拷贝对象`"}} java/io -.-> lab-117441{{"`浅拷贝与深拷贝对象`"}} java/object_methods -.-> lab-117441{{"`浅拷贝与深拷贝对象`"}} end

创建需要复制的对象

我们将首先创建一个 Student 类,该类包含一个 GPA(用户自定义类)类型的成员变量,用于存储学生的 GPA。

class GPA {
    int firstYear;
    int secondYear;

    public GPA(int firstYear, int secondYear) {
        this.firstYear = firstYear;
        this.secondYear = secondYear;
    }

    public int getFirstYear() {
        return this.firstYear;
    }

    public int getSecondYear() {
        return this.secondYear;
    }

    public void setFirstYear(int firstYear) {
        this.firstYear = firstYear;
    }

    public void setSecondYear(int secondYear) {
        this.secondYear = secondYear;
    }
}

class Student {
    private String name;
    private GPA gpa;

    public Student(String name, GPA gpa) {
        this.name = name;
        this.gpa = gpa;
    }

    public String getName() {
        return this.name;
    }

    public GPA getGpa() {
        return this.gpa;
    }

    public void setName(String name) {
        this.name = name;
    }

    public void setGpa(GPA gpa) {
        this.gpa = gpa;
    }
}

实现 Cloneable 接口并重写 clone() 方法以实现浅拷贝

为了实现浅拷贝,我们将使用 Object 类提供的 clone() 方法。必须实现 Cloneable 接口才能使用 clone() 方法,因为这是一个受保护的方法。我们只需在 Student 类中调用 clone() 方法即可执行浅拷贝。

class Student implements Cloneable {
    // ...

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

使用 clone() 方法实现深拷贝

我们可以通过重写 clone() 方法来实现深拷贝,如下所示。在 clone() 方法中,我们将使用 new 关键字创建一个新对象,并将原始对象的值赋给新对象。一旦我们有了新对象,我们将为所有不包含基本类型的引用类创建新对象,并分配新的引用而不是原始引用。

class Student implements Cloneable {
    // ...

    @Override
    public Object clone() throws CloneNotSupportedException {
        Student newStudent = (Student)super.clone();
        newStudent.gpa = (GPA)this.gpa.clone();
        return newStudent;
    }
}

class GPA implements Cloneable {
    // ...

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

实现拷贝构造函数

另一种创建深拷贝的方法是使用拷贝构造函数。拷贝构造函数是一种以同一类的对象作为输入并返回具有相同值但新引用的新对象的构造函数。我们将为 Student 类创建一个拷贝构造函数。

class Student {
    // ...

    public Student(Student student) {
        this(student.getName(), new GPA(student.getGpa().getFirstYear(), student.getGpa().getSecondYear()));
    }
}

实现序列化

序列化也提供了一种创建深拷贝的方法。通过序列化对象然后反序列化,我们可以生成一个具有新引用但相同值的新对象。为此,我们必须在需要深拷贝的类中实现 Serializable 接口。

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;

class GPA implements Serializable {
    // ...
}

class Student implements Serializable {
    // ...

    public Student deepCopyUsingSerialization() {
        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 (Student)ois.readObject();
        } catch(Exception e) {
            return null;
        }
    }
}

测试

现在我们可以测试不同的深拷贝方法,如下面的代码所示。

public static void main(String[] args) {
    GPA gpa = new GPA(7, 8);
    Student original = new Student("John", gpa);

    // Shallow copy
    Student shallowCopy = null;
    try {
        shallowCopy = (Student)original.clone();
    } catch(CloneNotSupportedException e) {
        e.printStackTrace();
    }

    // Deep copy using clone()
    Student cloneCopy = null;
    try {
        cloneCopy = (Student)original.clone();
    } catch(CloneNotSupportedException e) {
        e.printStackTrace();
    }

    // Deep copy using copy constructor
    Student constructorCopy = new Student(original);

    // Deep copy using serialization
    Student serializationCopy = original.deepCopyUsingSerialization();

    // Test for deep copying
    System.out.println("Original object: \t\t" + original.getGpa().getFirstYear() + " " + original.getGpa().getSecondYear());
    System.out.println("Shallow copy: \t\t\t" + shallowCopy.getGpa().getFirstYear() + " " + shallowCopy.getGpa().getSecondYear());
    System.out.println("Clone copy: \t\t\t" + cloneCopy.getGpa().getFirstYear() + " " + cloneCopy.getGpa().getSecondYear());
    System.out.println("Copy constructor copy: \t" + constructorCopy.getGpa().getFirstYear() + " " + constructorCopy.getGpa().getSecondYear());
    System.out.println("Serialization copy: \t\t" + serializationCopy.getGpa().getFirstYear() + " " + serializationCopy.getGpa().getSecondYear());

    cloneCopy.setGpa(new GPA(10, 9));
    System.out.println("\nAfter changing cloneCopy's GPA field: ");
    System.out.println("Original object: \t\t" + original.getGpa().getFirstYear() + " " + original.getGpa().getSecondYear());
    System.out.println("Clone copy: \t\t\t" + cloneCopy.getGpa().getFirstYear() + " " + cloneCopy.getGpa().getSecondYear());
}

运行

执行以下命令后:

javac Lab.java && java Lab

你将看到以下输出:

Original object: 		7 8
Shallow copy: 			7 8
Clone copy: 			7 8
Copy constructor copy: 	7 8
Serialization copy: 		7 8

After changing cloneCopy's GPA field:
Original object: 		7 8
Clone copy: 			10 9

总结

在本实验中,我们学习了如何在 Java 中实现对象的深拷贝。浅拷贝只是将原始对象的字段值复制到新对象中,而深拷贝会创建一个新对象,并为所有包含的对象创建新的独立引用。我们可以通过使用 clone() 方法、拷贝构造函数或序列化来实现深拷贝。在处理具有引用类对象时,进行深拷贝非常重要,以避免意外行为。

您可能感兴趣的其他 Java 教程