Java light copy and deep copy

Object copy in Java refers to copying all attributes (member variables) of an object to another object with the same class type. For example, object a and object B belong to class S and have attributes a and B. then copy object a and assign value to object B: B.a=A.a; B.b=A.b;

It is very common to copy objects in programs, mainly to reuse part or all of the data of existing objects in a new context.

Object copy in Java is mainly divided into shallow copy and deep copy.

First of all, let's introduce some foreshadowing knowledge: data types in Java are divided into basic data types and reference data types. For these two data types, there is a difference between value passing and reference (address) passing when performing assignment operation, being used as method parameter or return value.

Shallow Copy (Shallow Copy): ① for a member variable whose data type is a basic data type, Shallow Copy will directly transfer the value, that is, copy the attribute value to a new object. Because it is two different data, modifying the member variable value of one object will not affect the data copied by the other object. ② for a data type, it is a reference If the member variable of a data type is an array or an object of a class, the Shallow Copy will be passed by reference, That is, just copy the reference value (memory address) of the member variable to the new object. In fact, the member variable of two objects points to the same instance. In this case, modifying the member variable in one object will affect the member variable value of another object.

There are three main ways to implement shallow copy:

1, Shallow copy through copy construction method:

Copy constructor means that the constructor parameter of this class is the object of this class. Using the copy construction method can well complete the shallow copy, and directly create a new object with the same attributes as an existing object.

/* Copy construction method to realize shallow copy */
public class CopyConstructor {
    public static void main(String[] args) {
        Age a=new Age(20);
        Person p1=new Person(a,"Shake your head Jesus");
        Person p2=new Person(p1);
        System.out.println("p1 yes"+p1);
        System.out.println("p2 yes"+p2);
        //Modify the attribute values of p1 and observe whether the attribute values of p2 change with each other
        p1.setName("Little fool");
        a.setAge(99);
        System.out.println("Modified p1 yes"+p1);
        System.out.println("Modified p2 yes"+p2);
    }
}

class Person{
    //Two attribute values: Value Passing and reference passing
    private Age age;
    private String name;
    public Person(Age age,String name) {
        this.age=age;
        this.name=name;
    }
    //Copy construction method
    public Person(Person p) {
        this.name=p.name;
        this.age=p.age;
    }
    
    public void setName(String name) {
        this.name=name;
    }
    
    public String toString() {
        return this.name+" "+this.age;
    }
}

class Age{
    private int age;
    public Age(int age) {
        this.age=age;
    }
    
    public void setAge(int age) {
        this.age=age;
    }
    
    public int getAge() {
        return this.age;
    }
    
    public String toString() {
        return getAge()+"";
    }
}

The operation result is:

p1 is Jesus shaking his head 20
p2 is Jesus shaking his head 20
The modified p1 is little fool 99
The modified p2 is 99

Result analysis: two representative attribute values are selected for the Person class: one is the reference passing type; The other is a string type (constant).

The shallow copy is made through the copy construction method, and the attribute values are successfully copied. When the attribute value of p1 value transmission part changes, p2 will not change; When the value of some attributes passed by reference changes, p2 also changes.

Note: in the copy construction method, if you open up new memory space for the referenced data type variables one by one and create new objects, you can also realize deep copy. For the general copy structure, it must be a shallow copy.

2, Shallow copy by overriding the clone() method:

The Object class is the root class of the class structure. One of the methods is protected Object clone() throws CloneNotSupportedException , this method is a shallow copy. With this shallow copy template, we can implement the shallow copy of objects by calling the clone () method. Note: 1. Although the Object class has this method, However, this method is protected (modified by protected), so we can't use it directly. 2. The class using the clone method must implement the clonable interface, or an exception CloneNotSupportedException will be thrown. For these two points, our solution is to override the clone () method in the class using the clone method through super clone() calls the original clone method in the Object class.

Code example: copy the object of Student class, directly override the clone() method, and complete the shallow copy by calling the clone method.

/* clone Method to implement shallow copy */
public class ShallowCopy {
    public static void main(String[] args) {
        Age a=new Age(20);
        Student stu1=new Student("Shake your head Jesus",a,175);
        
        //Make a shallow copy by calling the overridden clone method
        Student stu2=(Student)stu1.clone();
        System.out.println(stu1.toString());
        System.out.println(stu2.toString());
        
        //Try to modify the attributes of stu1 and observe whether the attributes of stu2 have changed
        stu1.setName("Big fool");
        //Change the value of the member variable of this reference type
        a.setAge(99);
        //stu1.setaAge(new Age(99));     If you modify the age attribute value in this way, stu2 will not change with it. Because a new age class object is created instead of changing the instance value of the original object
        stu1.setLength(216);
        System.out.println(stu1.toString());
        System.out.println(stu2.toString());
    }
}

/*
 * Create age class
 */
class Age{
    //Member variable (attribute) of age class
    private int age;
    //Construction method
    public Age(int age) {
        this.age=age;
    }
    
    public int getAge() {
        return age;
    }
    
    public void setAge(int age) {
        this.age = age;
    }
    
    public String toString() {
        return this.age+"";
    }
}
/*
 * Create student class
 */
class Student implements Cloneable{
    //The member variable (attribute) of the student class, one of which is the object of the class
    private String name;
    private Age aage;
    private int length;
    //Construction method, in which one parameter is an object of another class
    public Student(String name,Age a,int length) {
        this.name=name;
        this.aage=a;
        this.length=length;
    }
    //alt+shift+s in eclipse automatically adds all set and get methods
    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
    
    public Age getaAge() {
        return this.aage;
    }
    
    public void setaAge(Age age) {
        this.aage=age;
    }
    
    public int getLength() {
        return this.length;
    }
    
    public void setLength(int length) {
        this.length=length;
    }
    //Sets the string form of the output
    public String toString() {
        return "The name is: "+this.getName()+", Age: "+this.getaAge().toString()+", The length is: "+this.getLength();
    }
    //Override the clone method of the Object class
    public Object clone() {
        Object obj=null;
        //Call the clone method of the Object class to return an Object instance
        try {
            obj= super.clone();
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
        }
        return obj;
    }
}

The operation results are as follows:

The name is: Jesus, age: 20, length: 175
The name is: Jesus, age: 20, length: 175
The name is: big fool, age: 99, length: 216
The name is: Jesus, age: 99, length: 175

Among them, I typically set three kinds of member variables of Student class: member variable length of basic data type, member variable aage of reference data type and name of String type

The analysis results can verify:

The basic data type is value transfer, so modifying the value will not affect the attribute value of another object;

The reference data type is address passing (reference passing), so after modifying the value, the property value of another object will be modified synchronously.

String type is very special, so I set an additional member variable of string type to explain. First of all, string type is a reference data type, not a basic data type, but the data of string type is stored in the constant pool, that is, it cannot be modified! In other words, when I change the name attribute from "shaking head Jesus" to "big fool", instead of modifying the value of the data, I change the reference of the data from the constant pointing to "shaking head Jesus" to the constant pointing to "big fool". In this case, the value of the name attribute of another object still pointing to "shaking head Jesus" will not be affected.

Deep copy: imagine that a class has an object, and its member variables have another object, which points to another object, and another object points to another object until a certain instance. This forms the object graph. Then, for deep copy, you should not only copy the member variable values of all basic data types of the object, but also apply for storage space for all member variables of reference data types, and copy the objects referenced by each member variable of reference data type until all objects that can be reached by the object. In other words, the whole object graph should be copied for deep copy of objects!

Simply put, deep copy opens up memory space for all objects in the object graph that refer to member variables of data types; The shallow copy only passes the address point, and the new object does not create memory space for the reference data type.

There are two main methods to implement deep copy:

1, Implement deep copy by overriding the clone method

Just like the basic idea of implementing shallow copy by rewriting clone method, it is necessary to implement Cloneable interface and rewrite clone method for every object of each layer of object diagram. Finally, deep clone can be achieved by calling all clone methods in the rewritten clone method of the top level class. Simply put, each object in each layer is subject to shallow copy = deep copy.

Example code:

package linearList;
/* Hierarchical call clone method to implement deep copy */
public class DeepCopy {
    public static void main(String[] args) {
        Age a=new Age(20);
        Student stu1=new Student("Shake your head Jesus",a,175);
        
        //Make a shallow copy by calling the overridden clone method
        Student stu2=(Student)stu1.clone();
        System.out.println(stu1.toString());
        System.out.println(stu2.toString());
        System.out.println();
        
        //Try to modify the attributes of stu1 and observe whether the attributes of stu2 have changed
        stu1.setName("Big fool");
        //Change the value of the member variable of this reference type
        a.setAge(99);
        //stu1.setaAge(new Age(99));     If you modify the age attribute value in this way, stu2 will not change with it. Because a new age class object is created instead of changing the instance value of the original object
        stu1.setLength(216);
        System.out.println(stu1.toString());
        System.out.println(stu2.toString());
    }
}

/*
 * Create age class
 */
class Age implements Cloneable{
    //Member variable (attribute) of age class
    private int age;
    //Construction method
    public Age(int age) {
        this.age=age;
    }
    
    public int getAge() {
        return age;
    }
    
    public void setAge(int age) {
        this.age = age;
    }
    
    public String toString() {
        return this.age+"";
    }
    
    //Override the clone method of Object
    public Object clone() {
        Object obj=null;
        try {
            obj=super.clone();
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
        }
        return obj;
    }
}
/*
 * Create student class
 */
class Student implements Cloneable{
    //The member variable (attribute) of the student class, one of which is the object of the class
    private String name;
    private Age aage;
    private int length;
    //Construction method, in which one parameter is an object of another class
    public Student(String name,Age a,int length) {
        this.name=name;
        this.aage=a;
        this.length=length;
    }
    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
    
    public Age getaAge() {
        return this.aage;
    }
    
    public void setaAge(Age age) {
        this.aage=age;
    }
    
    public int getLength() {
        return this.length;
    }
    
    public void setLength(int length) {
        this.length=length;
    }
    public String toString() {
        return "The name is: "+this.getName()+", Age: "+this.getaAge().toString()+", The length is: "+this.getLength();
    }
    //Override the clone method of the Object class
    public Object clone() {
        Object obj=null;
        //Call clone method of Object class -- shallow copy
        try {
            obj= super.clone();
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
        }
        //Call the clone method of the Age class for deep copy
        //First convert obj into a student class instance
        Student stu=(Student)obj;
        //The Age object attribute of the student class instance and call its clone method to copy
        stu.aage=(Age)stu.getaAge().clone();
        return obj;
    }
}

The name is: Jesus, age: 20, length: 175
The name is: Jesus, age: 20, length: 175
The name is: big fool, age: 99, length: 216
The name is: Jesus, age: 20, length: 175

The analysis results can verify that after deep copy, no matter what type of attribute value modification, it will not affect the attribute value of another object.

2, Deep copy through object serialization

Although calling the clone method hierarchically can achieve deep copy, it is obvious that the amount of code is too large. Especially for classes with a large number of attributes and deep levels, it is too cumbersome to rewrite the clone method for each class.

After the object is serialized into byte sequence, the whole object graph of the object will be serialized by default, and then the deep copy can be perfectly realized through de sequence.

Example code:

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;

/* Deep copy through serialization */
public class DeepCopyBySerialization {
    public static void main(String[] args) throws IOException, ClassNotFoundException  {
        Age a=new Age(20);
        Student stu1=new Student("Shake your head Jesus",a,175);
        //Deep copy through serialization
        ByteArrayOutputStream bos=new ByteArrayOutputStream();
        ObjectOutputStream oos=new ObjectOutputStream(bos);
        oos.writeObject(stu1);
        oos.flush();
        ObjectInputStream ois=new ObjectInputStream(new ByteArrayInputStream(bos.toByteArray()));
        Student stu2=(Student)ois.readObject();
        System.out.println(stu1.toString());
        System.out.println(stu2.toString());
        System.out.println();
        //Try to modify the attributes of stu1 and observe whether the attributes of stu2 have changed
        stu1.setName("Big fool");
        //Change the value of the member variable of this reference type
        a.setAge(99);
        stu1.setLength(216);
        System.out.println(stu1.toString());
        System.out.println(stu2.toString());
    }
}

/*
 * Create age class
 */
class Age implements Serializable{
    //Member variable (attribute) of age class
    private int age;
    //Construction method
    public Age(int age) {
        this.age=age;
    }
    
    public int getAge() {
        return age;
    }
    
    public void setAge(int age) {
        this.age = age;
    }
    
    public String toString() {
        return this.age+"";
    }
}
/*
 * Create student class
 */
class Student implements Serializable{
    //The member variable (attribute) of the student class, one of which is the object of the class
    private String name;
    private Age aage;
    private int length;
    //Construction method, in which one parameter is an object of another class
    public Student(String name,Age a,int length) {
        this.name=name;
        this.aage=a;
        this.length=length;
    }
    //alt+shift+s in eclipse automatically adds all set and get methods
    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
    
    public Age getaAge() {
        return this.aage;
    }
    
    public void setaAge(Age age) {
        this.aage=age;
    }
    
    public int getLength() {
        return this.length;
    }
    
    public void setLength(int length) {
        this.length=length;
    }
    //Sets the string form of the output
    public String toString() {
        return "The name is: "+this.getName()+", Age: "+this.getaAge().toString()+", The length is: "+this.getLength();
    }
}

The operation result is:

The name is: Jesus, age: 20, length: 175
The name is: Jesus, age: 20, length: 175
The name is: big fool, age: 99, length: 216
The name is: Jesus, age: 20, length: 175

Deep copy can be perfectly realized through very simple code., If an attribute is modified by transient, the attribute cannot be copied.

Keywords: Java

Added by west4me on Tue, 14 Dec 2021 22:01:59 +0200