Before officially entering the topic, let’s first understand the concepts of deep copy and pre-copy:
Light copy:
A new object will be created, which has an exact copy of the original object's property value. If the attribute is a basic type, the value of the basic type is copied; if the attribute is a memory address, the memory address is copied, so if an object changes this address, it will affect another object;
Deep copy:
Not only do you need to copy all non-referenced member variable values of the object, but you also need to create a new instance for member variables of the reference type and initialize it to the formal parameter instance value;
After understanding the concept, let’s test whether ordinary object assignment operations are deep copy or shallow copy:
Test code:
public class DepthCopy { public static void main(String[] args) { Copy first = new Copy("hzw", 24); Copy second = first; second.name = "shanxi"; System.out.println(first.name);//Output shanxi } } class Copy { public String name; public int age; public Copy(String name,int age) { this.name = name; this.age = age; } } It can be found that after second modifying the name attribute value to shanxi, the first name attribute value also becomes shanxi. This shows that ordinary object assignments belong to shallow copies;
After understanding whether the assignment between objects is a shallow copy, let’s see whether the cloning is a deep copy or a shallow copy. The test code is to enable the above Copy object to implement the clone method in the Cloneable interface:
public class DepthCopy { public static void main(String[] args) { Copy first = new Copy("hzw", 24); Copy second = null; try { second = (Copy) first.clone(); } catch (CloneNotSupportedException e) { e.printStackTrace(); } second.name = "shanxi"; System.out.println(first.name);//Output: hzw System.out.println(first);//Output: com.hzw.day33.Copy@7f39ebdb System.out.println(second);//Output: com.hzw.day33.Copy@33abb81e } } class Copy implements Cloneable { public String name; public int age; public Copy(String name,int age) { this.name = name; this.age = age; } @Override protected Object clone() throws CloneNotSupportedException { return super.clone(); } }It can be seen that the originally created object first and the cloned object second are two instances, so the modification of the name attribute in second will not affect the name attribute in first; however, we cannot simply think that cloning is a deep copy, such as the following example:
public class DepthCopy { public static void main(String[] args) { Student student = new Student(95); Copy first = new Copy("hzw", 24,student); Copy second = null; try { second = (Copy) first.clone(); } catch (CloneNotSupportedException e) { e.printStackTrace(); } second.name = "shanxi"; second.student.score = 60; System.out.println(first == second);//false System.out.println(first.student == second.student);//true System.out.println(first.student.score);//60 } } class Copy implements Cloneable { public String name; public int age; public Student student; public Copy(String name,int age,Student student) { this.name = name; this.age = age; this.student = student; } @Override protected Object clone() throws CloneNotSupportedException { return super.clone(); } } class Student { public int score; public Student(int score) { this.score = score; } }Have you seen it? We created second through cloning, and it is obvious that first and second are two instances, because the output of first == second is false, but the student objects in first and second are the same. After modifying the score value of student through second, the score of student in first also changed. This means that the student in first and second is the same. This means that cloning is a shallow copy. If we want to implement a deep copy of cloning, we must let the Student object in the Copy object also implement the clone method in the Cloneable interface, and the clone method in Copy returns a clone of Student, so that the student can be unique. The modified code is as follows:
public class DepthCopy { public static void main(String[] args) { Student student = new Student(95); Copy first = new Copy("hzw", 24,student); Copy second = null; try { second = (Copy) first.clone(); } catch (CloneNotSupportedException e) { e.printStackTrace(); } second.name = "shanxi"; second.student.score = 60; System.out.println(first == second);//false System.out.println(first.student == second.student);//false System.out.println(first.student.score);//95 System.out.println(second.student.score);//60 } } class Copy implements Cloneable { public String name; public int age; public Student student; public Copy(String name,int age,Student student) { this.name = name; this.age = age; this.student = student; } @Override protected Object clone() throws CloneNotSupportedException { Copy copy = (Copy)super.clone(); copy.student = (Student) student.clone(); return copy; } } class Student implements Cloneable { public int score; public Student(int score) { this.score = score; } @Override protected Object clone() throws CloneNotSupportedException { return super.clone(); } } You can see that first and second, first.student and second.student are not the same at this time. Therefore, after we modify the score of second student, it does not affect the score value of student in the first, achieving the purpose of deep copying;
However, if you think about it carefully, the problem will arise. If there are also reference types attributes in the Student class in our example above, such as the College class, then we must let the College class implement the Cloneable interface, and then call the College class clone method in the clone method in the Student class, and call the Student class clone method in the clone method of the Copy class. I found that it was gone. This process is so complicated. All the relevant reference types in the class must implement the Cloneable interface. I feel it is so troublesome, okay, the next thing is to be awesome.
The best way to solve the deep copy problem is to use serialization, so that all classes do not need to implement the Cloneable interface, just serialize and deserialize directly. Let's take a look.
import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.Serializable; public class DepthCopy { public static void main(String[] args) { College school = new College("nongda"); Student student = new Student(95, school); Copy copy = new Copy("hzw",23, student); Copy another = null;//Indicate the deserialized class instance//Serialize the serialization operation try { FileOutputStream fos = new FileOutputStream(new File("d:/copy.txt")); ObjectOutputStream oos = new ObjectOutputStream(fos); oos.writeObject(copy); } catch (Exception e) { e.printStackTrace(); } //Serialize the deserialization operation FileInputStream fis; try { fis = new FileInputStream(new File("d:/copy.txt")); ObjectInputStream ois = new ObjectInputStream(fis); another = (Copy) ois.readObject(); } catch (Exception e) { e.printStackTrace(); } System.out.println(copy == another);//false System.out.println(copy.student == another.student);//false System.out.println(copy.student.school == another.student.school);//false another.student.school.schoolName = "wuda"; System.out.println(copy.student.school.schoolName);//nongda } } class Copy implements Serializable { public String name; public int age; public Student student; public Copy(String name,int age,Student student) { this.name = name; this.age = age; this.student = student; } } class Student implements Serializable { public int score; public College school; public Student(int score,College school) { this.score = score; this.school = school; } } class College implements Serializable { public String schoolName; public College(String schoolName) { this.schoolName = schoolName; } } From the output, we can see that the object generated after deserialization is a copy of the original object, and it does not have any relationship with the original object except for the same attribute value. Therefore, when we modify the schoolName of the deserialization generated object to "wuda", we did not modify the schoolName of the original instance, and we still output "nongda", so we achieved the real deep copy effect. However, in order to achieve serialization, all relevant classes must implement the Serializable interface, which is always more convenient than implementing both the Cloneable interface and the clone method.
The above is a detailed explanation of Java deep and shallow copies. If you need it, please refer to it.