Shallow cloning and deep cloning

Posted by actionsports on Tue, 15 Feb 2022 07:00:36 +0100

1, Shallow clone

In shallow cloning, if the member variable of the prototype object is a value type, a copy will be copied to the cloned object; If the member variable of the prototype object is a reference type, copy the address of the reference object to the clone object, that is, the member variables of the prototype object and the clone object point to the same memory address.

Simply put, in shallow cloning, when an object is copied, only its own and the member variables of the value type contained in it are copied, while the member object of the reference type is not copied.

2, Deep clone

In deep clone, no matter whether the member variable of the prototype object is a value type or a reference type, it will be copied to the cloned object. Deep clone will also copy all reference objects of the prototype object to the cloned object.

Simply put, in deep cron, in addition to the object itself being copied, all member variables contained in the object will also be copied.

In Java, if you need to implement deep cloning, you can overwrite it Object class The clone() implementation of can also be realized through serialization and other methods.

If the reference type also contains many reference types, or the class of the inner reference type contains reference types, it will be troublesome to use the clone method. At this time, the deep cloning of objects can be realized by serialization.

Serialization is the process of writing an object to a stream. The object written to the stream is a copy of the original object, which still exists in memory. The copy realized by serialization can copy not only the object itself, but also the member object referenced by it. Therefore, deep cloning can be realized by writing the object to a stream through serialization and reading it out from the stream. It should be noted that for objects that can be serialized, their classes must implement the Serializable interface, otherwise the serialization operation cannot be realized.

The code of clonable interface and Serializable interface provided by Java is very simple. They are both empty interfaces. This empty interface is also called identification interface. There is no definition of any method in the identification interface. Its purpose is to tell JRE whether the implementation classes of these interfaces have a certain function, such as whether they support cloning, serialization, etc.

3, Solve the problem of multi-layer cloning

public class Outer implements Serializable{
  private static final long serialVersionUID = 369285298572941L;  //It is better to explicitly declare the ID
  public Inner inner;
 //Description: [deep copy method, which requires serialization of the object and all object attributes of the object]
  public Outer myclone() {
      Outer outer = null;
      try {
//Serialize the object into a stream, because what is written in the stream is a copy of the object,
//The original object still exists in the JVM. Therefore, this feature can be used to realize the deep copy of objects
          ByteArrayOutputStream baos = new ByteArrayOutputStream();
          ObjectOutputStream oos = new ObjectOutputStream(baos);
          oos.writeObject(this);
      // Serialize streams into objects
          ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
          ObjectInputStream ois = new ObjectInputStream(bais);
          outer = (Outer) ois.readObject();
      } catch (IOException e) {
          e.printStackTrace();
      } catch (ClassNotFoundException e) {
          e.printStackTrace();
      }
      return outer;
  }
}

Inner must also implement Serializable, otherwise it cannot be serialized:

public class Inner implements Serializable{
  private static final long serialVersionUID = 872390113109L; //It is better to explicitly declare the ID
  public String name = "";
  public Inner(String name) {
      this.name = name;
  }
  @Override
  public String toString() {
      return "Inner of name The value is:" + name;
  }
}

This also enables two objects to exist completely independently in memory space without affecting each other's values.

4, Understand

Suppose you want to copy a simple variable. It's simple:

int apples = 5;
int pears = apples;

Not only the int type, the other seven basic data types (byte, short, char, long, float, double, boolean) are also applicable to this kind of situation. However, if you copy an object, the situation is somewhat complicated:

class Student {
    private int number;
    public int getNumber() {
        return number;
    }
    public void setNumber(int number) {
        this.number = number;
    }
}
public class Test {
    public static void main(String args[]) {
        Student stu1 = new Student();
        stu1.setNumber(12345);
        Student stu2 = stu1;
        System.out.println("Student 1:" + stu1.getNumber());
        System.out.println("Student 2:" + stu2.getNumber());
    }  
}

result:
Student 1:12345
Student 2:12345

Try to change the number field of stu2 instance and print the result:

stu2.setNumber(54321);
System.out.println("Student 1:" + stu1.getNumber()); 
System.out.println("Student 2:" + stu2.getNumber());

result:
Student 1:54321
Student 2:54321

This is strange. Why did the student number of student 2 and student 1 change? The reason lies in the sentence stu2 = stu1. The function of this sentence is to assign the reference of stu1 to stu2, so that stu1 and stu2 point to the same object in the memory heap. As shown in the figure:

So, how can I copy an object? Object. It has 11 methods and two protected methods, one of which is the clone method. All classes in Java inherit the object class by default. Check the source code and find one protected Method clone():

/*
Creates and returns a copy of this object. The precise meaning of "copy" may 
depend on the class of the object.
The general intent is that, for any object x, the expression:
1) x.clone() != x will be true
2) x.clone().getClass() == x.getClass() will be true, but these are not absolute requirements.
3) x.clone().equals(x) will be true, this is not an absolute requirement.
*/
protected native Object clone() throws CloneNotSupportedException;

It is a native method. The native method is the code implemented in non Java language and can be called by Java programs. Because Java programs run on the JVM virtual machine, there is no way to access the underlying operating system related, which can only be realized by the language close to the operating system.

  1. For the first time, it is guaranteed that the cloned object will have a separate memory address allocation.
  2. The second shows that the original and cloned objects should have the same class type, but it is not mandatory.
  3. The third shows that the original and cloned objects should be used equally, but it is not mandatory.

Because the direct or indirect parent class of each class is Object, they all contain clone(), but because the method is protected, it cannot be accessed outside the class. To copy an Object, you need to override the clone method.

5, Why do I need to clone objects? Can't you just new an object?

The cloned object may contain some modified properties, and the properties of the new object are still the values at the time of initialization. Therefore, when a new object is needed to save the "state" of the current object, the clone method is used. So isn't it OK to assign the temporary properties of this object to the new new object one by one? Yes, but trouble. Moreover, clone is a native method in the source code, which is extremely convenient.

Object a=new Object();
Object b;
b=a;

This form of code copies the reference, that is, the address of the object in memory. A and b objects still point to the same object. When a clone object is assigned independently of the original method, it exists at the same time.

6, How to clone

In Java, data type It is divided into value type (basic data type) and reference type. Value types include byte, short, char, int, long, float, double and boolean simple data types; Reference types include complex types such as classes, interfaces and arrays. The main difference between deep clone and shallow clone lies in whether they support deep clone.

one ️⃣ Shallow cloning

  1. The copied class needs to implement the Clonenable interface (if not, clonenotsupportedexception will be thrown when calling the clone method), which is a marked interface (without any methods).
  2. Override clone(), and set the access modifier to public. Call super. in the method Clone() gets the object to be copied. (native is a local method)

The above method is modified as follows:

class Student implements Cloneable{  
    private int number;  
    public int getNumber() {  
        return number;  
    }  
    public void setNumber(int number) {  
        this.number = number;  
    }  
      
    @Override  
    public Object clone() {  
        Student stu = null;  
        try{  
            stu = (Student)super.clone();  
        }catch(CloneNotSupportedException e) {  
            e.printStackTrace();  
        }  
        return stu;  
    }  
}  
public class Test {  
    public static void main(String args[]) {  
        Student stu1 = new Student();  
        stu1.setNumber(12345);  
        Student stu2 = (Student)stu1.clone();  
          
        System.out.println("Student 1:" + stu1.getNumber());  
        System.out.println("Student 2:" + stu2.getNumber());  
          
        stu2.setNumber(54321);  
      
        System.out.println("Student 1:" + stu1.getNumber());  
        System.out.println("Student 2:" + stu2.getNumber());  
        System.out.println(stu1 == stu2); // false
    }  
}

result:
Student 1:12345
Student 2:12345
Student 1:12345
Student 2:54321
false ---- indicates that the two objects are not the same object

two ️⃣ Deep cloning

Add an Address class to the student class.

class Address  {
    private String add;
    public String getAdd() {
        return add;
    }
    public void setAdd(String add) {
        this.add = add;
    }
}
class Student implements Cloneable{
    private int number;
    private Address addr;
    public Address getAddr() {
        return addr;
    }
    public void setAddr(Address addr) {
        this.addr = addr;
    }
    public int getNumber() {
        return number;
    }
    public void setNumber(int number) {
        this.number = number;
    }

    @Override
    public Object clone() {
        Student stu = null;
        try{
            stu = (Student)super.clone();
        }catch(CloneNotSupportedException e) {
            e.printStackTrace();
        }
        return stu;
    }
}
public class Test {
    public static void main(String args[]) {
        Address addr = new Address();
        addr.setAdd("Hangzhou");
        Student stu1 = new Student();
        stu1.setNumber(123);
        stu1.setAddr(addr);
        Student stu2 = (Student)stu1.clone();
        System.out.println("Student 1:" + stu1.getNumber() + ",Address:" + stu1.getAddr().getAdd());
        System.out.println("Student 2:" + stu2.getNumber() + ",Address:" + stu2.getAddr().getAdd());
    }
}

result:
Student 1:123, address: Hangzhou
Student 2:123, address: Hangzhou

Try to change the address of the addr instance in the main method:

addr.setAdd("West Lake District");  
System.out.println("Student 1:" + stu1.getNumber() + ",Address:" + stu1.getAddr().getAdd());
System.out.println("Student 2:" + stu2.getNumber() + ",Address:" + stu2.getAddr().getAdd());

result:
Student 1:123, address: Hangzhou
Student 2:123, address: Hangzhou
Student 1:123, address: West Lake District
Student 2:123, address: West Lake District

The reason is that shallow copy only copies the reference of addr variable and does not really open up another space. Copy the value and then return the reference to the new object. Therefore, in order to achieve real replication objects, rather than pure reference replication. The Address class needs to be replicable and the clone method needs to be modified. The complete code is as follows:

class Address implements Cloneable {
    private String add;
    public String getAdd() {
        return add;
    }
    public void setAdd(String add) {
        this.add = add;
    }

    @Override
    public Object clone() {
        Address addr = null;
        try{
            addr = (Address)super.clone();
        }catch(CloneNotSupportedException e) {
            e.printStackTrace();
        }
        return addr;
    }
}

class Student implements Cloneable{
    private int number;
    private Address addr;
    public Address getAddr() {
        return addr;
    }
    public void setAddr(Address addr) {
        this.addr = addr;
    }
    public int getNumber() {
        return number;
    }
    public void setNumber(int number) {
        this.number = number;
    }

    @Override
    public Object clone() {
        Student stu = null;
        try{
            stu = (Student)super.clone();   //Shallow replication
        }catch(CloneNotSupportedException e) {
            e.printStackTrace();
        }
        stu.addr = (Address)addr.clone();   //Deep replication
        return stu;
    }
}
public class Test {
    public static void main(String args[]) {
        Address addr = new Address();
        addr.setAdd("Hangzhou");
        Student stu1 = new Student();
        stu1.setNumber(123);
        stu1.setAddr(addr);
        Student stu2 = (Student)stu1.clone();
        System.out.println("Student 1:" + stu1.getNumber() + ",Address:" + stu1.getAddr().getAdd());
        System.out.println("Student 2:" + stu2.getNumber() + ",Address:" + stu2.getAddr().getAdd());
        addr.setAdd("West Lake District");
        System.out.println("Student 1:" + stu1.getNumber() + ",Address:" + stu1.getAddr().getAdd());
        System.out.println("Student 2:" + stu2.getNumber() + ",Address:" + stu2.getAddr().getAdd());
    }
}

result:
Student 1:123, address: Hangzhou
Student 2:123, address: Hangzhou
Student 1:123, address: West Lake District
Student 2:123, address: Hangzhou

Finally, take a look at one of the classes in the API that implements the clone method: Java util. Date:

/** 
 * Return a copy of this object. 
 */  
public Object clone() {  
    Date d = null;  
    try {  
        d = (Date)super.clone();  
        if (cdate != null) {  
            d.cdate = (BaseCalendar.Date) cdate.clone();  
        }  
    } catch (CloneNotSupportedException e) {} // Won't happen  
    return d;  
}

This class also belongs to deep replication.

7, Summary

There are two ways to implement object cloning:
one ️⃣ Implement the clonable interface and override clone() in the Object class.
two ️⃣ Realize the Serializable interface and realize cloning through object serialization and deserialization, which can realize real deep cloning.
three ️⃣ Note: cloning based on serialization and deserialization is not only deep cloning, but more importantly, it can check whether the Object to be cloned supports serialization through generic restriction. This check is completed by the compiler rather than throwing an exception at runtime. This scheme is obviously better than cloning objects using the clone method of Object class. Exposing problems at compile time is always better than leaving problems at run time.

Topics: Java