Creation of objects in Java
Clone is copied as its name implies, and in the Java language, the Clone method is called by the object, so the object is copied. The so-called Replication object, the first to allocate a source object and the same size of space, in this space to create a new object. So in the Java language, there are several ways to create objects?
1 Creating an object using the new operator
2 Copying an object using the Clone method
So what are the similarities and differences between these two approaches? The new operator is intended to allocate memory. When the program executes to the new operator, it first looks at the type following the new operator because it knows the type to know how much memory space to allocate. After allocating memory, call the constructor, populate the object's fields, this step is called the initialization of the object, after the construction method is returned, an object is created, you can publish his reference (address) to the outside, you can use this reference to manipulate the object externally. and clone in the first step is similar to new, is to allocate memory, when calling the Clone method, the allocated memory and the source object (that is, the object called the Clone method) is the same, and then use the corresponding fields in the original object, populate the new object's domain, after the completion of the Clone method returns, A new, identical object is created, and the reference to the new object can be published externally.
Copy object or copy reference
In Java, code similar to the following is very common:
New Person ("Zhang"= p; SYSTEM.OUT.PRINTLN (P); System.out.println (p1);
When person p1 = P; After execution, is a new object created? First look at the print results:
[Email protected] [Email protected]
As you can see, the printed address values are the same, and since the addresses are the same, it must be the same object. P and P1 are just references, and they all point to a person of the same object ("Zhang"). This phenomenon can be referred to as reference replication. (For reference and object differentiation, you can refer to my previous article why is string in Java immutable?) -string source analysis, which has a section on reference and object differentiation. After the execution of the above code is complete, the in-memory scenario looks like this:
And the following code is really a true clone of an object.
New Person ("Zhang"); = (person) p.clone (); SYSTEM.OUT.PRINTLN (p); System.out.println (p1);
As you can see from the printout, the address of two objects is different, that is, creating a new object instead of assigning the address of the original object to a new reference variable:
[Email protected] [Email protected]
When the above code finishes executing, the in-memory scenario looks like this:
Deep copy or shallow copy
In the example code above, there are two member variables in person, name and age, name is a string type, and age is an int type. The code is very simple, as follows:
Public classPersonImplementscloneable{Private intAge ; PrivateString name; PublicPerson (intAge , String name) { This. Age =Age ; This. Name =name; } PublicPerson () {} Public intGetage () {returnAge ; } PublicString GetName () {returnname; } @OverrideprotectedObject Clone ()throwsclonenotsupportedexception {return(person)Super. Clone (); }}
Since age is the basic data type, there is no doubt about its copy, just copy a 4-byte integer value. But name is a string type, it is just a reference, 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. Either create a new identical string object based on the string object that is pointed to in the original person object, and assign a reference to the new string object to the name field of the newly copied person object. These two copies are called Light and deep copies, respectively. The principle of deep copy and shallow copy is as follows:
The following is verified by code. If the address value of the name of the two person object is the same, the name of both objects points to the same string object, which is a shallow copy, and if the address value of the name of the two object is different, then a different string object is pointed to. That is, when the person object is copied, the string object referenced by name is also copied, that is, a deep copy. The verification code is as follows:
New Person ("Zhang"); = (person) p.clone (); = P.getname () ==? "Clone is a shallow copy": "Clone is a deep copy"; SYSTEM.OUT.PRINTLN (result);
Printing results are:
Clone is a shallow copy.
Therefore, the Clone method performs a shallow copy, which should be noted in writing the program.
Overwrite the Clone method in object to achieve a deep copy
Now in order to make a deep copy of the Clone object, it is necessary to clonable the interface, overwrite and implement the Clone method, except to invoke the Clone method in the parent class to get the new object, and also clone the reference variable in the class. If you only use the default Clone method in object, which is a shallow copy, verify again with the following code:
Static classBodyImplementscloneable{ Publichead Head; PublicBody () {} PublicBody (head head) { This. Head =Head;} @OverrideprotectedObject Clone ()throwsclonenotsupportedexception {return Super. Clone (); } } Static classHead/*implements Cloneable*/{ PublicFace face ; PublicHead () {} PublicHead (face face) { This. face =Face ;} } Public Static voidMain (string[] args)throwsclonenotsupportedexception {Body Body=NewBody (NewHead ()); 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 you clone a Body object, it combines a face object with only a shallow copy. Printing results can verify the conclusion:
Falsetrue
If you want to make a deep copy of the body object in clone, then in the body's clone method, the head object referenced by the source object is also clone a copy.
Static classBodyImplementscloneable{ Publichead Head; PublicBody () {} PublicBody (head head) { This. Head =Head;} @OverrideprotectedObject Clone ()throwsclonenotsupportedexception {Body newbody= (Body)Super. Clone (); Newbody.head=(Head) Head.clone (); returnNewbody; } } Static classHeadImplementscloneable{ PublicFace face ; PublicHead () {} PublicHead (face face) { This. face =Face ;} @OverrideprotectedObject Clone ()throwsclonenotsupportedexception {return Super. Clone (); } } Public Static voidMain (string[] args)throwsclonenotsupportedexception {Body Body=NewBody (NewHead ()); Body body1=(Body) Body.clone (); System.out.println ("BODY = = Body1:" + (BODY = =body1)); System.out.println ("Body.head = = Body1.head:" + (Body.head = =body1.head)); }
Printing results are:
Falsefalse
Thus, the head reference within body and body1 points to a different head object, which means that at the same time as the Clone body object, it also copies the head object it refers to and makes a deep copy.
Is it really a deep copy?
The following conclusions can be drawn from the previous section: If you want to make a deep copy of an object, this object must implement the Cloneable interface, implement the Clone method, and, within the Clone method, copy the other objects referenced by the object to a clone, This 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, in order to deep copy the body class, must be in the Body class clone method will also copy the Head class, but in the copy head class, the default is to perform a shallow copy, That is, the face object combined in the head is not copied. The verification code is as follows: (here is the code for the face class only, but to be consistent in reading, to avoid losing contextual information, or to give the entire program, the whole program is very brief)
Static classBodyImplementscloneable{ Publichead Head; PublicBody () {} PublicBody (head head) { This. Head =Head;} @OverrideprotectedObject Clone ()throwsclonenotsupportedexception {Body newbody= (Body)Super. Clone (); Newbody.head=(Head) Head.clone (); returnNewbody; } } Static classHeadImplementscloneable{ PublicFace face ; PublicHead () {} PublicHead (face face) { This. face =Face ;} @OverrideprotectedObject Clone ()throwsclonenotsupportedexception {return Super. Clone (); } } Static classface{} Public Static voidMain (string[] args)throwsclonenotsupportedexception {Body Body=NewBody (NewHead (NewFace ())); 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)); }
Printing results are:
Falsefalsetrue
The memory structure diagram looks like this:
So, is this a deep copy of the body object? In fact, it should be a deep copy, since the other objects referenced within the Body object (currently only head) are copied, meaning that the head reference within two separate body objects has pointed to a separate two head object. However, for the two head objects, they point to the same face object, which means that two body objects still have a certain connection and are not completely independent of each other. This should be said to be an incomplete deep copy.
How to make a thorough deep copy
For the above example, how can you ensure that two body objects are completely independent? As long as the head object is copied, the face object will be copied a copy of it. This requires that the face class also implement the Cloneable interface, implement the Clone method, and copy the face object it refers to in the Clone method of the head object. The modified part of the code is as follows:
Static classHeadImplementscloneable{ PublicFace face ; PublicHead () {} PublicHead (face face) { This. face =Face ;} @OverrideprotectedObject Clone ()throwsclonenotsupportedexception {//return Super.clone (); Head Newhead = (head)Super. Clone (); Newhead.face= (face) This. Face.clone (); returnNewhead; } } Static classFaceImplementscloneable{@OverrideprotectedObject Clone ()throwsclonenotsupportedexception {return Super. Clone (); } }
Running the above example again, the results are as follows:
falsefalse
This says that the name two body is completely independent, the face object that they indirectly refer to has been copied, that is, refers to the independent face object. The memory structure diagram is as follows:
And so on, if the face object also references other objects, such as mouth, if it is not processed, the body object is copied and referenced to the same mouth object by a first-level reference. Similarly, if you want the body to be completely independent on the reference chain, you can only explicitly make the mouth object copy.
Here, you can get the following conclusion: If you want to copy an object and the source object is completely independent of each other, then each level of object on the reference chain must be explicitly copied. Therefore, it is very troublesome to create a thorough deep copy, especially if the reference relationship is very complex, or if a third party object is referenced at some level of the reference chain, and the object does not implement the Clone method, then all referenced objects after it are shared. For example, if the face class referenced by the head is a class in a third-party library, and the Cloneable interface is not implemented, then all objects after the face are referenced together by the two body objects before and after the copy. Assume that the mouth object is assembled inside a face object, and that the mouth object is composed of tooth objects, with memory structures such as:
Written in the last
Cloning may not be very frequent in the development of a project, but distinguishing between deep and shallow copies gives us a deeper understanding of how Java memory is structured and run. As for thorough deep copy, it is almost impossible to achieve, for reasons already described in the previous section. Deep and deep copies, when creating immutable objects, can have subtle effects on the program and may determine whether immutable objects we create are really immutable. An important application of clone is also used for the creation of immutable objects. For the creation of immutable objects, I will explain in the subsequent articles, please look forward to.
A detailed explanation of the Clone method in Java: Prototype mode