A colleague who has been working for three years can't figure out deep copy or shallow copy...


Object copying is basically a rigid requirement when we write code every day, and we often encounter it, but many people are busy writing business every day, ignoring some detailed problems and understanding. Sometimes once there is a problem in this area, it is not easy to troubleshoot Up.

So this article will sort out.

Note: This article has been included in the Github open source project: github.com/hansonwang99/JavaCollection . There are detailed self-learning programming learning routes, interview questions and interviews , programming materials and series of technical articles, etc. The resources are continuously updated...


Value type vs reference type

The accurate distinction between these two concepts is very important for the understanding of deep and shallow copy problems.

As the title of the second chapter of Java" JavaProgramming Thoughts" in the Bible says, Javaeverything can be regarded as an object!

So in Javathe world we have come to , we have to get used to using references to manipulate objects. Among them Java, such as arrays, classes Class, enumerations Enum, Integerpackaging classes, etc., are typical reference types, so in general, the method of passing by reference is used when operating ;

However Java, language-level basic data types, such as intthese basic types, are generally operated by value transfer , so they are sometimes called value types.

In order to facilitate the following descriptions and examples, we first define two categories: Studentand Major, which represent "students" and "professionals" respectively. The two are inclusive relationships:

// 学生的所学专业
public class Major {
    private String majorName; // 专业名称
    private long majorId;     // 专业代号
    
    // ... 其他省略 ...
}
// 学生
public class Student {
    private String name;  // 姓名
    private int age;      // 年龄
    private Major major;  // 所学专业
    
    // ... 其他省略 ...
}

Assignment vs. shallow copy vs. deep copy

Object assignment

Assignment is the most common operation in the daily programming process, the simplest such as:

Student codeSheep = new Student();
Student codePig = codeSheep;

Strictly speaking, this cannot be regarded as an object copy, because the copy is only the reference relationship, and does not generate a new actual object:

Shallow copy

Shallow copy is a type of object cloning, and its important characteristics are reflected in the   word "shallow" .

For example, we try to studen1copy through examples. student2If it is a shallow copy, the rough model can be shown as follows:

Obviously, the value type field will be copied, and the reference type field will only copy the reference address, and the actual object space pointed to by the reference address is actually only one copy.

A picture is better than the preface, I think the above picture has been very clear.

Deep copy

Deep copy is compared to the shallow copy shown above, except that the value type field will be copied, and the object pointed to by the reference type field will also be created in memory , just like this:

The principle is very clear, let's take a look at the specific code implementation.


Shallow copy code implementation

In the above example, I want to get it by student1copying student2. The typical implementation of shallow copy is: let the class of the copied object implement the Cloneableinterface and rewrite the clone()method.

Take the above Studentclass copy as an example:

public class Student implements Cloneable {

    private String name;  // 姓名
    private int age;      // 年龄
    private Major major;  // 所学专业

    @Override
    public Object clone() throws CloneNotSupportedException {
        return super.clone();
    }
    
    // ... 其他省略 ...

}

Then we write a test code, you will know after a try:

public class Test {

    public static void main(String[] args) throws CloneNotSupportedException {

        Major m = new Major("计算机科学与技术",666666);
        Student student1 = new Student( "CodeSheep", 18, m );
        
        // 由 student1 拷贝得到 student2
        Student student2 = (Student) student1.clone();

        System.out.println( student1 == student2 );
        System.out.println( student1 );
        System.out.println( student2 );
        System.out.println( "\n" );

        // 修改student1的值类型字段
        student1.setAge( 35 );
        
        // 修改student1的引用类型字段
        m.setMajorName( "电子信息工程" );
        m.setMajorId( 888888 );

        System.out.println( student1 );
        System.out.println( student2 );

    }
}

Run and get the following results:

It can be seen from the results:

  • student1==student2Print false, indicating that the clone()method has indeed cloned a new object;

  • Modifying the value type field does not affect the new cloned object, which is in line with expectations;

  • The student1internal reference object is modified , and the cloned object student2is also affected, indicating that the internal is still connected.


Deep copy code implementation

Deep traversal copy

Although the clone()method can complete the copying of the object, note that the clone()method defaults to a shallow copy behavior, just like the example above. If you want to implement a deep copy, you need to override the  clone()method to implement a deep traversal copy of the referenced object and perform a carpet search.

So for the above example, if you want to implement deep copy, you first need to Majortransform the deeper reference class , let it also implement the Cloneableinterface and rewrite the clone()method:

public class Major implements Cloneable {

    @Override
    protected Object clone() throws CloneNotSupportedException {
        return super.clone();
    }
    
    // ... 其他省略 ...
}

Secondly, we also need to rewrite the method in the top-level calling class cloneto call the clone()method of the reference type field to achieve deep copy. Corresponding to this article, it is the Studentclass:

public class Student implements Cloneable {

    @Override
    public Object clone() throws CloneNotSupportedException {
        Student student = (Student) super.clone();
        student.major = (Major) major.clone(); // 重要!!!
        return student;
    }
    
    // ... 其他省略 ...
}

At this time, the above test case is unchanged, and the results can be obtained by running:

Obviously, at this time student1and the student2two objects are completely independent, free from mutual interference.

Use deserialization to achieve deep copy

I remember that in the previous article "Serialization/Deserialization, I have to bear you for a long time", I have sorted out and summarized the knowledge points of "serialization and deserialization" in detail.

Using deserialization technology, we can also deep copy from one object to another copy object, and this product is surprisingly effective in solving the problem of deep copying of multi-layer dolls.

So let's modify the Studentclass here and let its clone()method generate a deep copy of the original object through serialization and deserialization:

public class Student implements Serializable {

    private String name;  // 姓名
    private int age;      // 年龄
    private Major major;  // 所学专业

    public Student clone() {
        try {
            // 将对象本身序列化到字节流
            ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
            ObjectOutputStream objectOutputStream =
                    new ObjectOutputStream( byteArrayOutputStream );
            objectOutputStream.writeObject( this );

            // 再将字节流通过反序列化方式得到对象副本
            ObjectInputStream objectInputStream =
                    new ObjectInputStream( new ByteArrayInputStream( byteArrayOutputStream.toByteArray() ) );
            return (Student) objectInputStream.readObject();

        } catch (IOException e) {
            e.printStackTrace();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }

        return null;
    }
    
    // ... 其他省略 ...
}

Of course, in this case, the referenced subclass (such as the Majorclass here ) must also be serializable, that is, it implements the Serializableinterface:

public class Major implements Serializable {
  
  // ... 其他省略 ...
    
}

At this time, the test case is completely unchanged. If you run it directly, you can also get the following results:

Obviously, this time student1and the student2two objects are also completely independent, without mutual interference, and the deep copy is completed.


postscript

Well, let's talk about the issue of "deep copy" and "shallow copy" this time. I thought that this article would be finished soon, but I pulled out so many things, but after sorting out and connecting in this way, I still feel a lot clearer.

—————END—————

Friends who like this article, welcome to follow the official account  programmer Xiaohui , and watch more exciting content

点个[在看],是对小灰最大的支持!

Guess you like

Origin blog.csdn.net/bjweimengshu/article/details/108373217