clone shallow copy and deep copy in java

Reprinted from: https://blog.csdn.net/zhangjg_blog/article/details/18369201

Object Creation in Java


clone, as the name implies, is to copy. In the Java language, the clone method is called by the object, so the object will be copied. The so-called copy object first allocates a space of the same size as the source object, and creates a new object in this space. So in the java language, there are several ways to create objects?

1 Use the new operator to create an object
2 Use the clone method to copy an object

So what are the similarities and differences between these two methods? The purpose of the new operator is to allocate memory. When the program executes the new operator, it first looks at the type behind the new operator, because only by knowing the type can it know how much memory space to allocate. After allocating the memory, call the constructor to fill in the various fields of the object. This step is called initialization of the object. After the constructor returns, an object is created, and its reference (address) can be published to the outside, which can be used outside. This reference manipulates this object. The first step of clone is similar to new in that it allocates memory. When calling the clone method, the allocated memory is the same as the source object (that is, the object that calls the clone method), and then uses the corresponding fields in the original object to fill in After the field of the new object is filled, the clone method returns, a new identical object is created, and a reference to this new object can also be released to the outside world.



Copy object or copy reference


In Java, code like the following is very common:
        
        Person p = new Person(23, "zhang");
        Person p1 = p;

        System.out.println(p);
        System.out.println(p1);
        

When Person p1 = p; is executed, is a new object created? First look at the print result:
com.pansoft.zhangjg.testclone.Person@2f9ee1ac
com.pansoft.zhangjg.testclone.Person@2f9ee1ac

It can be seen that the printed address values ​​are the same. Since the addresses are the same, they must be the same object. p and p1 are just references, they both point to the same object Person(23, "zhang"). This phenomenon can be called copying of references. (For the distinction between references and objects, please refer to my previous article Why is String in Java Immutable? - String Source Code Analysis , which has a section on the distinction between references and objects). After the above code is executed, the scene in the memory is shown in the following figure:

And the following code is a real clone of an object.
        Person p = new Person(23, "zhang");
        Person p1 = (Person) p.clone();

        System.out.println(p);
        System.out.println(p1);

It can be seen from the printed results that the addresses of the two objects are different, that is to say, a new object is created instead of assigning the address of the original object to a new reference variable:
com.pansoft.zhangjg.testclone.Person@2f9ee1ac
com.pansoft.zhangjg.testclone.Person@67f1fba0

After the above code is executed, the scene in the memory is shown in the following figure:




deep copy or shallow copy


In the above sample code, Person has two member variables, namely name and age, name is of type String, and age is of type int. The code is very simple and looks like this:
public class Person implements Cloneable{

    private int age ;
    private String name;

    public Person(int age, String name) {
        this.age = age;
        this.name = name;
    }

    public Person() {}

    public int getAge() {
        return age;
    }

    public String getName() {
        return name;
    }

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


Since age is a basic data type, there is no doubt about its copy, just copy a 4-byte integer value directly. But name is of type String, it is just a reference, pointing to a real String object, then there are two ways to copy it: directly copy the reference value of name in the source object to the name field of the new object, or according to The string object pointed to by name in the original Person object creates a new identical string object, and assigns a reference to the new string object to the name field of the newly copied Person object. These two copy methods are called shallow copy and deep copy, respectively. The principle of deep copy and shallow copy is shown in the following figure:


Verify the code below. If the address values ​​of the names of the two Person objects are the same, it means that the names of the two objects point to the same String object, that is, a shallow copy, and if the address values ​​of the names of the two objects are different, it means that they point to different String objects , that is, when copying the Person object, the String object referenced by name is also copied, that is, a deep copy. The verification code is as follows:
        Person p = new Person(23, "zhang");
        Person p1 = (Person) p.clone();

        String result = p.getName() == p1.getName()
                ? "clone is a shallow copy" : "clone is a deep copy";

        System.out.println(result);



The print result is:

clone is a shallow copy

Therefore, the clone method performs a shallow copy, so pay attention to this detail when writing programs .


Override the clone method in Object to implement deep copy


Now, in order to make a deep copy when the object is cloned, the Clonable interface needs to be overridden and implemented. In addition to calling the clone method in the parent class to obtain a new object, the reference variable in the class should also be cloned. If you just use the default clone method in Object, which is a shallow copy, verify again with the following code:

    static class Body implements Cloneable{
        public Head head;

        public Body() {}

        public Body(Head head) {this.head = head;}

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

    }
    static class Head /*implements Cloneable*/{
        public  Face face;

        public Head() {}
        public Head(Face face){this.face = face;}

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

        Body body = new Body(new Head());

        Body body1 = (Body) body.clone();

        System.out.println("body == body1 : " + (body == body1) );

        System.out.println("body.head == body1.head : " +  (body.head == body1.head));


    }

In the above code, there are two main classes, Body and Face, respectively. In the Body class, a Face object is combined. When a Body object is cloned, only a shallow copy of its combined Face object is made. This conclusion can be verified by printing the result:
body == body1 : false
body.head == body1.head : true

If you want to make a deep copy of the Body object when it is cloned, you need to clone the Head object referenced by the source object in the clone method of the Body.

    static class Body implements Cloneable{
        public Head head;
        public Body() {}
        public Body(Head head) {this.head = head;}

        @Override
        protected Object clone() throws CloneNotSupportedException {
            Body newBody =  (Body) super.clone();
            newBody.head = (Head) head.clone();
            return newBody;
        }

    }
    static class Head implements Cloneable{
        public  Face face;

        public Head() {}
        public Head(Face face){this.face = face;}
        @Override
        protected Object clone() throws CloneNotSupportedException {
            return super.clone();
        }
    }
    public static void main(String[] args) throws CloneNotSupportedException {

        Body body = new Body(new Head());

        Body body1 = (Body) body.clone();

        System.out.println("body == body1 : " + (body == body1) );

        System.out.println("body.head == body1.head : " +  (body.head == body1.head));


    }

The print result is:
body == body1 : false
body.head == body1.head : false

It can be seen that the head references in body and body1 point to different Head objects, that is, when the Body object is cloned, the Head object it refers to is also copied, and a deep copy is performed.


Is it really a deep copy?


From the content of the previous section, the following conclusions can be drawn: if you want to deeply copy an object, the object must implement the Cloneable interface, implement the clone method, and inside the clone method, other objects referenced by the object must also be cloned. , which requires that the referenced object must also implement the Cloneable interface and implement the clone method.

Then, according to the above conclusion, the Body class combines the Head class, and the Head class combines the Face class. If you want to deeply copy the Body class, you must also copy the Head class in the clone method of the Body class, but copy the Head class. When using a class, a shallow copy is performed by default, which means that the combined Face object in the Head will not be copied. The verification code is as follows: (Originally, only the code of the Face class is given here, but in order to read it coherently and avoid losing context information, the whole program is still given, and the whole program is also very short)
    static class Body implements Cloneable{
        public Head head;
        public Body() {}
        public Body(Head head) {this.head = head;}

        @Override
        protected Object clone() throws CloneNotSupportedException {
            Body newBody =  (Body) super.clone();
            newBody.head = (Head) head.clone();
            return newBody;
        }

    }

    static class Head implements Cloneable{
        public  Face face;

        public Head() {}
        public Head(Face face){this.face = face;}
        @Override
        protected Object clone() throws CloneNotSupportedException {
            return super.clone();
        }
    }

    static class Face{}

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

        Body body = new Body(new Head(new Face()));

        Body body1 = (Body) body.clone();

        System.out.println("body == body1 : " + (body == body1) );

        System.out.println("body.head == body1.head : " +  (body.head == body1.head));

        System.out.println("body.head.face == body1.head.face : " +  (body.head.face == body1.head.face));


    }

The print result is:
body == body1 : false
body.head == body1.head : false
body.head.face == body1.head.face : true

The memory structure diagram is shown in the following figure:


So, is this a deep copy for the Body object? In fact, it should be regarded as a deep copy, because the other objects (currently only Head) referenced in the Body object are copied, that is to say, the head references in the two independent Body objects have already pointed to two independent Head objects. However, for the two Head objects, they point to the same Face object, which means that the two Body objects still have a certain relationship and are not completely independent. This should be said to be an incomplete deep copy .


How to do a thorough deep copy


For the above example, how can we ensure that the two Body objects are completely independent? As long as you copy the Head object, you can also copy the Face object. This requires the Face class to also implement the Cloneable interface, implement the clone method, and in the clone method of the Head object, copy the Face object it references. The modified part of the code is as follows:
    static class Head implements Cloneable{
        public  Face face;

        public Head() {}
        public Head(Face face){this.face = face;}
        @Override
        protected Object clone() throws CloneNotSupportedException {
            //return super.clone();
            Head newHead = (Head) super.clone();
            newHead.face = (Face) this.face.clone();
            return newHead;
        }
    }

    static class Face implements Cloneable{
        @Override
        protected Object clone() throws CloneNotSupportedException {
            return super.clone();
        }
    }

Run the above example again and get the following results:
body == body1 : false
body.head == body1.head : false
body.head.face == body1.head.face : false

This means that the two bodies are completely independent, and the face objects they indirectly refer to have been copied, that is, they refer to independent Face objects. The memory structure diagram is as follows:




By analogy, if the Face object also references other objects, such as Mouth, if it is not processed, the Body object will still be referenced to the same Mouth object through level-by-level references after being copied. Similarly, if you want the Body to be completely independent on the reference chain, you can only explicitly copy the Mouth object.
At this point, the following conclusion can be drawn: if when copying an object, if the copied object and the source object are to be completely independent of each other, then each level of the object in the reference chain must be explicitly copied. Therefore , it is very troublesome to create a thorough deep copy, especially when the reference relationship is very complex, or a third-party object is referenced at a certain level of the reference chain, and this object does not implement the clone method, then in it All subsequent referenced objects are shared. For example, if the Face class referenced by Head is a class in a third-party library and does not implement the Cloneable interface, then all objects after Face will be referenced by the two Body objects before and after the copy. Assuming that the Mouth object is combined inside the Face object, and the Tooth object is combined inside the Mouth object, the memory structure is as follows:




write at the end


Clone may not be used very often in the development of normal projects, but distinguishing between deep copy and shallow copy will give us a deeper understanding of java memory structure and operation mode. As for thorough deep copy, deep copy and thorough deep copy, when creating immutable objects, there may be subtle effects on the program, which may determine whether the immutable objects we create are really immutable. An important application of clone is also for the creation of immutable objects.

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=325932982&siteId=291194637