First, the introduction
Object copy is the copying of an object's properties to another object that has the same class type. Copying objects in a program is common, primarily to reuse some or all of the object's data in a new context. There are three types of object copies in Java: Shallow copy (shallow copy), deep copy (Deep copy), deferred copy (Lazy copy).
Second, shallow copy
1, what is a shallow copy
A shallow copy is a bitwise Copy object that creates a new object that has an exact copy of the original object's attribute value. If the attribute is a basic type, the copy is a value of the base type, and if the property is a memory address (a reference type), the copy is the memory address, so if one of the objects changes the address, it affects the other object.
In the diagram, SourceObject has an int type property "Field1" and a reference type property "Refobj" (an object that references a containedobject type). When you make a shallow copy of SourceObject, you create a copiedobject that has a property "Field2" containing the "field1" copy value and a reference that still points to the refobj itself. Because "Field1" is a basic type, it simply copies its value to "Field2", but because "refobj" is a reference type, Copiedobject points to the same address as "Refobj". Therefore, any changes to the "Refobj" in the SourceObject will affect the copiedobject.
2. How to realize shallow copy
Here is an example of implementing a shallow copy
Copy Code code as follows:
public class Subject {
private String name;
Public Subject (String s) {
name = S;
}
Public String GetName () {
return name;
}
public void SetName (String s) {
name = S;
}
}
public class Student implements cloneable {
Object reference
Private Subject subj;
private String name;
Public Student (String s, String sub) {
name = S;
SUBJ = new Subject (sub);
}
Public Subject getsubj () {
return subj;
}
Public String GetName () {
return name;
}
public void SetName (String s) {
name = S;
}
/**
* Override Clone () method
* @return
*/
Public Object Clone () {
Shallow copy
try {
The Clone () method that calls the parent class directly
return Super.clone ();
catch (Clonenotsupportedexception e) {
return null;
}
}
}
public class Copytest {
public static void Main (string[] args) {
Original Object
Student stud = new Student ("John", "algebra");
System.out.println ("Original Object:" + stud.getname () + "-" + STUD.GETSUBJ (). GetName ());
Copying objects
Student Clonedstud = (Student) stud.clone ();
System.out.println ("cloned Object:" + clonedstud.getname () + "-" + CLONEDSTUD.GETSUBJ (). GetName ());
Whether the original object and the copied object are the same:
System.out.println (' Is Original object ' same with cloned object: + (stud = = Clonedstud));
Whether the original object and the Copy object's Name property are the same
System.out.println ("is Original object") field name the same with cloned object: "+
(Stud.getname () = = Clonedstud.getname ());
Whether the original object and the Copy object have the same Subj property
System.out.println ("Is Original object" field subj the same with cloned object: "+
(STUD.GETSUBJ () = = CLONEDSTUD.GETSUBJ ());
Stud.setname ("Dan");
STUD.GETSUBJ (). SetName ("Physics");
System.out.println ("Original Object after the It is updated:" + stud.getname () + "-" +
STUD.GETSUBJ (). GetName ());
System.out.println ("cloned object after updating original object:" + clonedstud.getname () +
"-" + CLONEDSTUD.GETSUBJ (). GetName ());
}
}
The output results are as follows:
Original Object:john-algebra
Cloned Object:john-algebra
is Original Object the same with cloned Object:false
is Original Object ' s field name "Same with cloned object:true
is Original Object ' s field subj the same with cloned object:true
Original Object after it is updated:dan-physics
Cloned Object after updating original object:john-physics
In this example, I let the class student the copy implement the Clonable interface and rewrite the Clone () method of the object class, and then call the Super.clone () method inside the method. From the output, we can see that the changes made to the "name" attribute of the original object stud did not affect the Copy object Clonedstud, but the change to the "name" attribute of the Reference object SUBJ affected the Copy object Clonedstud.
Third, deep copy
1. What is deep copy
A deep copy copies all the attributes and copies the dynamically allocated memory that the attribute points to. A deep copy occurs when the object is copied with the object it refers to. Deep copies are slower and more expensive than shallow copies.
In the above illustration, SourceObject has an int type property "Field1" and a reference type property "RefObj1" (an object that references a containedobject type). When a deep copy of SourceObject is made, the Copiedobject is created with a property "Field2" containing the "field1" copy value and a reference type property "RefObj1" that contains the "RefObj2" copy value. Therefore, any changes made to the "Refobj" in SourceObject will not affect the Copiedobject
2. How to realize deep copy
Here is an example of implementing a deep copy. Just a little change in the example of a shallow copy, Subject and Copytest classes are unchanged.
Copy Code code as follows:
public class Student implements cloneable {
Object reference
Private Subject subj;
private String name;
Public Student (String s, String sub) {
name = S;
SUBJ = new Subject (sub);
}
Public Subject getsubj () {
return subj;
}
Public String GetName () {
return name;
}
public void SetName (String s) {
name = S;
}
/**
* Override Clone () method
*
* @return
*/
Public Object Clone () {
Deep copy, creating a new object for the copy class, which is independent of the original object
Student s = new Student (name, Subj.getname ());
return s;
}
}
The output results are as follows:
Original Object:john-algebra
Cloned Object:john-algebra
is Original Object the same with cloned Object:false
is Original Object ' s field name "Same with cloned object:true
is Original Object ' s field subj the same with cloned Object:false
Original Object after it is updated:dan-physics
Cloned Object after updating original Object:john-algebra
It is easy to find a little change in the Clone () method. Because it is a deep copy, you need to create an object of the copy class. Because there is an object reference in the student class, you need to implement the Cloneable interface in the student class and override the Clone method.
3, through the serialization of the implementation of deep copy
Deep copies can also be achieved by serialization. What does serialization do? It writes the entire object graph into a persistent storage file and reads it back when needed, meaning that you need a copy of the entire object graph when you need to read it back. This is what you really need when you make a deep copy of an object. Note that when you make a deep copy through serialization, you must make sure that all the classes in the object graph are serializable.
Copy Code code as follows:
public class Coloredcircle implements Serializable {
private int x;
private int y;
public coloredcircle (int x, int y) {
this.x = x;
This.y = y;
}
public int GetX () {
return x;
}
public void SetX (int x) {
this.x = x;
}
public int GetY () {
return y;
}
public void sety (int y) {
This.y = y;
}
@Override
Public String toString () {
Return "x=" + x + ", y=" + y;
}
}
public class Deepcopy {
public static void Main (string[] args) throws IOException {
ObjectOutputStream oos = null;
ObjectInputStream ois = null;
try {
To create the original serializable object
Coloredcircle C1 = new Coloredcircle (100, 100);
System.out.println ("Original =" + C1);
Coloredcircle C2 = null;
Deep copy implemented by serialization
Bytearrayoutputstream BOS = new Bytearrayoutputstream ();
Oos = new ObjectOutputStream (BOS);
Serialization and passing this object
Oos.writeobject (C1);
Oos.flush ();
Bytearrayinputstream bin = new Bytearrayinputstream (Bos.tobytearray ());
OIS = new ObjectInputStream (BIN);
Returns a new object
C2 = (coloredcircle) ois.readobject ();
Verify that the content is the same
System.out.println ("Copied =" + C2);
Change the contents of the original object
C1.SETX (200);
C1.sety (200);
View every current content
System.out.println ("Original =" + C1);
System.out.println ("Copied =" + C2);
catch (Exception e) {
System.out.println ("Exception in main =" + E);
finally {
Oos.close ();
Ois.close ();
}
}
}
The output results are as follows:
Original = x=100, y=100
Copied = x=100, y=100
Original = x=200, y=200
Copied = x=100, y=100
Here, you only need to do the following few things:
(1) Ensure that all classes in the object graph are serializable
(2) Create an input output stream
(3) Use this input-output stream to create object input and object output streams
(4) Pass the object you want to copy to the object output stream
(5) Reading the new object from the object input stream and converting it back to the class of the object you are sending
In this example, I created a Coloredcircle object C1 and then serialized it (writes it to Bytearrayoutputstream). I then deserialize the serialized object and save it to C2. Then I modified the original object C1. And then as you can see, C1 is different from C2, and any changes you make to C1 will not affect C2.
Note that serialization has its own limitations and problems in this way:
Because the transient variable cannot be serialized, the transient variable cannot be copied using this method.
Then there is the performance problem. Creates a socket, serializes an object, transmits it through the socket, and deserializes it, which is slow compared to the method of invoking an existing object. So there's a big difference in performance. If performance is critical to your code, it is recommended that you do not use this approach. It takes almost 100 times times more time to make a deep copy than by implementing the Clonable interface.
Iv. delayed copies
Deferred copies are a combination of shallow copies and deep copies that are rarely used in practice. When you first copy an object, you use a faster shallow copy, and a counter is used to record how many objects share the data. When the program wants to modify the original object, it determines whether the data is shared (by checking the counter) and makes a deep copy as needed.
A delayed copy looks like a deep copy from the outside, but whenever possible it takes advantage of the speed of a shallow copy. A deferred copy can be used when the references in the original object do not change frequently. Due to the existence of counters, the efficiency drop is high, but only constant-level overhead. Also, in some cases, circular references can cause problems.
V. How to choose
If the object's properties are all basic types, you can use a shallow copy, but if the object has a reference attribute, select either a shallow copy or a deep copy based on the specific requirements. I mean if the object reference doesn't change at any time, then there's no need to use a deep copy, just use a shallow copy. If the object reference changes frequently, use a deep copy. There is no rigid rule, and everything depends on the specific needs.