You can understand it at a glance. java deep copy and shallow copy

Keywords: Java

There are three ways to copy a reference from one object to another. The first method is direct assignment, the second method is shallow copy, and the third method is deep copy. So you know, these three concepts are actually for copying objects.
 

1. Direct assignment

OK, let's look at the first method, direct assignment. In Java, A a1 = a2. We need to understand that this actually copies a reference, that is, a1 and a2 point to the same object. Therefore, when a1 changes, the member variables in a2 will also change. Ladies and gentlemen, please look at the following code!
 
/* Create class */  
class Resume {  
    private String name;  //full name  
    private String sex;   //Gender  
    private int age;      //Age  
    private String experience; //work experience  
      
    public Resume(String name, String sex, int age) {  
        this.name = name;  
        this.sex = sex;  
        this.age = age;  
    }  
      
    public void setAge(int age) {  
        this.age = age;  
    }  
    public int getAge() {  
        return age;  
    }  
      
    public void setExperience(String experience) {  
        this.experience = experience;  
    }  
    public String getExperience() {  
        return experience;  
    }  
      
    public void displayResume() {  
        System.out.println("full name:"+name+" Gender:"+sex+" Age:"+age);  
        System.out.println("Work experience:"+experience);  
    }  
}  
  
public class MainClass {  
    public static void main(String[] args) {  
        Resume zhangsan = new Resume("zhangsan","male",24);  
        zhangsan.setExperience("2009-2013 Studying at home and college, proficient in JAVA,C,C++,C#Wait for code copy ");  
        zhangsan.displayResume();  
        Resume zhangsan1 = zhangsan;  
        zhangsan1.setExperience("2009-2013 Studying at home and college, proficient in JAVA,C,C++,C#Etc.);  
        zhangsan.displayResume();  
        zhangsan1.displayResume();  
    }  
}  

Program running results

full name: zhangsan Gender: male age:24  
Work experience: 2009-2013 Studying at home and college, proficient in JAVA,C,C++,C#Wait for code replication  
full name: zhangsan Gender: male age:24  
Work experience: 2009-2013 Studying at home and college, proficient in JAVA,C,C++,C#etc.  
full name: zhangsan Gender: male age:24  
Work experience: 2009-2013 Studying at home and college, proficient in JAVA,C,C++,C#etc.  

In this program, a resume of Zhangsan is generated. Then I copied a resume zhangsan1. It can be seen that when the work experience in zhangsan1 changed, the work experience of Zhangsan also changed.

 

2. Shallow copy

Sometimes the result of direct assignment above may not be what we want. For example, when we submit resumes, we may make corresponding adjustments according to the type of company applied for. If we invest in technical work, we may be a little more technical; If it is invested in state-owned enterprises, social experience and student work may also be a very important part. So we don't need to change the tone of all resumes when we modify one resume. Otherwise, the companies that invest in technology will have to change back. Having said so much, we just hope that after assigning a1 to a2, a1 and a2 can remain independent and do not affect each other.
 
The method to implement one of the above ideas is the clone () function of Object. Here, we need to understand what clone () does, create a new Object, and then copy the non static field of the current Object to the new Object. If the field is of value type, copy the field; If the field is a reference type, the reference is copied, but the referenced Object is not copied. Therefore, the original Object and its copy reference the same Object.
OK, let's look at the first part of this paragraph. If the field is a value type, it will be copied directly. As shown in the following procedure
 
/* Create a class to implement the Clone method  */  
class Resume  implements Cloneable{  
    private String name;  //full name  
    private String sex;   //Gender  
    private int age;      //Age  
    private String experience; //work experience  
      
    public Resume(String name, String sex, int age) {  
        this.name = name;  
        this.sex = sex;  
        this.age = age;  
    }  
      
    public void setAge(int age) {  
        this.age = age;  
    }  
    public int getAge() {  
        return age;  
    }  
      
    public void setExperience(String experience) {  
        this.experience = experience;  
    }  
    public String getExperience() {  
        return experience;  
    }  
      
    public void displayResume() {  
        System.out.println("full name:"+name+" Gender:"+sex+" Age:"+age);  
        System.out.println("Work experience:"+experience);  
    }  
      
    public Object clone() {  
        try {  
            return (Resume)super.clone();  
        } catch (Exception e) {  
            e.printStackTrace();  
            return null;  
        }  
    }  
}  
  
public class MainClass {  
    public static void main(String[] args) {  
        Resume zhangsan = new Resume("zhangsan","male",24);  
        zhangsan.setExperience("2009-2013 Studying at home and college, proficient in JAVA,C,C++,C#Etc. ");  
        zhangsan.displayResume();  
        Resume zhangsan1 = (Resume)zhangsan.clone();  
        zhangsan1.setAge(23);  
        zhangsan1.displayResume();  
        Resume zhangsan2 = (Resume)zhangsan.clone();  
        zhangsan2.setExperience("2009-2013 Studying at home and college, proficient in JAVA,C,C++,C#Etc. ");  
        zhangsan2.displayResume();  
        zhangsan.displayResume();  
    }  
}  

Program running results

full name: zhangsan Gender: male age:24  
Work experience: 2009-2013 Studying at home and college, proficient in JAVA,C,C++,C#Wait for code copy and paste  
full name: zhangsan Gender: male age:23  
Work experience: 2009-2013 Studying at home and college, proficient in JAVA,C,C++,C#Wait for code copy and paste  
full name: zhangsan Gender: male age:24  
Work experience: 2009-2013 Studying at home and college, proficient in JAVA,C,C++,C#Other code  
full name: zhangsan Gender: male age:24  
Work experience: 2009-2013 Studying at home and college, proficient in JAVA,C,C++,C#Wait for code copy and paste  
From the running results of the program, we can see that we have realized the independence of a1 and a2 references.
 
But what is meant by "if the field is a reference type, the reference is copied but the referenced object is not copied. Therefore, the original object and its copy refer to the same object"? Don't worry, let's take a look at the following procedure:
class Experience {  
      
    private String educationBackground;  
    private String skills;  
      
    public void setExperience(String educationBackground, String skills) {  
        // TODO Auto-generated constructor stub  
        this.educationBackground = educationBackground;  
        this.skills = skills;  
    }  
    public String toString() {  
        return educationBackground + skills;  
    }  
}  
  
/* Create a class to implement the Clone method  */  
class Resume  implements Cloneable{  
    private String name;  //full name  
    private String sex;   //Gender  
    private int age;      //Age  
    private Experience experience; //work experience  
      
    public Resume(String name, String sex, int age) {  
        this.name = name;  
        this.sex = sex;  
        this.age = age;  
        this.experience = new Experience();  
    }  
      
    public void setAge(int age) {  
        this.age = age;  
    }  
    public int getAge() {  
        return age;  
    }  
      
    public Experience getExperience() {  
        return experience;  
    }  
      
    public void setExperience(String educationBackground, String skills) {  
        experience.setExperience(educationBackground, skills);  
    }  
      
    public void displayResume() {  
        System.out.println("full name:"+name+" Gender:"+sex+" Age:"+age);  
        System.out.println("Work experience:"+experience.toString());  
    }  
      
    public Object clone() {  
        try {  
            return (Resume)super.clone();  
        } catch (Exception e) {  
            e.printStackTrace();  
            return null;  
        }  
    }  
}  
  
public class MainClass {  
    public static void main(String[] args) {  
        Resume zhangsan = new Resume("zhangsan","male",24);  
        zhangsan.setExperience("2009-2013 Study at home and go to College","master JAVA,C,C++,C#Etc. ");  
        zhangsan.displayResume();  
  
        Resume zhangsan2 = (Resume)zhangsan.clone();  
        zhangsan2.setExperience("2009-2013 Study at home and go to College","master JAVA,C,C++,C#Etc.);  
        zhangsan2.displayResume();  
        zhangsan.displayResume();  
        zhangsan2.displayResume();  
    }  
}  

Program running results:

full name: zhangsan Gender: male age:24  
Work experience: 2009-2013 Study at home and master college JAVA,C,C++,C#Wait for code copy and paste  
full name: zhangsan Gender: male age:24  
Work experience: 2009-2013 Study at home and master college JAVA,C,C++,C#etc.  
full name: zhangsan Gender: male age:24  
Work experience: 2009-2013 Study at home and master college JAVA,C,C++,C#etc.  
full name: zhangsan Gender: male age:24  
Work experience: 2009-2013 Study at home and master college JAVA,C,C++,C#etc.  

Let's take a look at the differences between the above two programs. The work Experience of the first program is a common member variable of the Resume class, that is, the value attribute. In the later program, work Experience is a class. Combined with the running results of the above program, we can understand that "if the field is a reference type, the reference is copied, but the referenced object is not copied. Therefore, the original object and its copy refer to the same object." in fact, the Experience classes in Zhangsan and zhangsan2 point to the same object! Either the Experience change in Zhangsan or the Experience change in zhangsan2 will affect the other.

 

3. Deep copy

In fact, the key to the problem lies in the clone() method. We know that the clone() method uses the clone() method of the Object class, but this method has a defect. It does not copy all the attributes of the Object, but selectively. The basic rules are as follows:

       1. Basic type

         If the variable is a basic type, copy its value, such as int, float, etc.

       2. Object

         If the variable is an instance object, copy its address reference, that is, the new object and the original object share the instance variable.

       3. String string

         If the variable is a String, copy its address reference. However, when modifying, it will regenerate a new String from the String pool, and the original purple capital object will remain unchanged.

      Based on the above rules, we can easily find the problem. They share an object. If Zhang San modifies the content of the email, Li Si and Wang Wu will also modify it, so the above situation will occur. We can solve this problem. We just need to create an object in the clone() method, and then Zhang San references the object:

rotected Person clone() {
        Person person = null;
        try {
            person = (Person) super.clone();
            person.setEmail(new Email(person.getEmail().getObject(),person.getEmail().getContent()));
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
        }
        
        return person;
    }

Therefore: shallow copy is only a simple copy mechanism provided by Java, which is not convenient for direct use.

      There is still a problem with the above solution. If a large number of objects in our system are generated by copying, if we write a clone() method for each class, we will also need to make a deep copy and create a large number of objects. This project is very large. Here we can use serialization to copy objects.

 

How to use serialization to copy objects? It is easy to copy byte stream in memory. Write the parent object into a byte stream and read it from the byte stream. In this way, a new object can be created, and there is no reference sharing problem between the new object and the parent object, so as to truly realize the deep copy of the object.

public class CloneUtils {
    @SuppressWarnings("unchecked")
    public static <T extends Serializable> T clone(T obj){
        T cloneObj = null;
        try {
            //Write byte stream
            ByteArrayOutputStream out = new ByteArrayOutputStream();
            ObjectOutputStream obs = new ObjectOutputStream(out);
            obs.writeObject(obj);
            obs.close();
            
            //Allocate memory, write the original object, and generate a new object
            ByteArrayInputStream ios = new ByteArrayInputStream(out.toByteArray());
            ObjectInputStream ois = new ObjectInputStream(ios);
            //Returns the generated new object
            cloneObj = (T) ois.readObject();
            ois.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return cloneObj;
    }
}

Objects using this tool class must implement the Serializable interface, otherwise there is no way to clone.

public class Person implements Serializable{
    private static final long serialVersionUID = 2631590509760908280L;

    ..................
    //Remove clone() method

}

public class Email implements Serializable{
    private static final long serialVersionUID = 1267293988171991494L;
    
    ....................
}

Therefore, objects using this tool class can clone objects as long as they implement the Serializable interface, and there is no need to inherit the clonable interface to implement the clone() method.

public class Client {
    public static void main(String[] args) {
        //Write an email
        Email email = new Email("Please attend the meeting","Please contact today 12:30 Attend the meeting in conference room 2...");
        
        Person person1 =  new Person("Zhang San",email);
        
        Person person2 =  CloneUtils.clone(person1);
        person2.setName("Li Si");
        Person person3 =  CloneUtils.clone(person1);
        person3.setName("Wang Wu");
        person1.getEmail().setContent("Please contact today 12:00 Attend the meeting in conference room 2...");
        
        System.out.println(person1.getName() + "The content of your email is:" + person1.getEmail().getContent());
        System.out.println(person2.getName() + "The content of your email is:" + person2.getEmail().getContent());
        System.out.println(person3.getName() + "The content of your email is:" + person3.getEmail().getContent());
    }
}
-------------------
Output:
The content of Zhang San's email is: please contact today 12:00 Attend the meeting in conference room 2...
The content of Li Si's email is: please contact today 12:30 Attend the meeting in conference room 2...
The content of Wang Wu's email is: please contact today 12:30 Attend the meeting in conference room 2...

 

Posted by darf on Tue, 23 Nov 2021 02:41:35 -0800