clone() of Object in Java and Analysis of Shallow and Deep Copies

First, look at the source code:

 public class Object  {
     protected native Object clone() throws CloneNotSupportedException;
 }

From the source code we will find that:

First: clone() method of Object class is a native method, and the efficiency of native method is generally much higher than that of non-native method in Java. This also explains why the clone() method in Object is used instead of a new class, and then the information from the original object is copied into the new object, although this also implements the clone function. JNI is the abbreviation of Java Native Interface. Since Java 1.1, the Java Native Interface (JNI) standard has become part of the Java platform, allowing Java code to interact with code written in other languages. JNI was originally designed for locally compiled languages, especially C and C++, but it does not prevent you from using other languages, as long as the invocation convention is supported. Using java to interact with locally compiled code often loses platform portability. However, in some cases this is acceptable or even necessary, such as using some old libraries, interacting with hardware or operating system, or improving program performance. The JNI standard at least guarantees that local code works under any Java virtual machine implementation.)

Second, the clone() method in the Object class is modified by the protected modifier. This also means that if you want to apply clone() method, you must inherit Object classes. In Java, all classes inherit Object classes by default, so you don't have to care about that. Then overload the clone() method. Another consideration is that in order for other classes to call the clone() method of this clone class, the property of the clone() method should be set to public after overloading.

Third: The Object.clone() method returns an Object object. We have to do a forced type conversion to get the type we need.

The concepts of shallow replication and deep replication:

Shallow replication: All member attributes of the replicated object have the same value as the original object, while all references to other objects still point to the original object. In other words, shallow replication only replicates the object under consideration, not the object it refers to. (The concept is not easy to understand, please combine the following examples to understand)

Deep replication: All variables of the replicated object contain the same values as the original object, except those that refer to other objects. Variables that refer to other objects will point to the new objects that have been copied, rather than the original ones that have been referenced. In other words, objects referenced by objects to be duplicated are duplicated once in depth.

1. Shallow duplication

package other;

public class test3 {

    //Shallow replication
    public static void main(String[] args) throws CloneNotSupportedException {
        Dog dog = new Dog(10, "Liu Cheng Cheng");
        Animal a = new Animal(11, "Yan Hai bin", dog);
        System.out.println(a.getAge() + "," + a.getName() + "," + a.getDog().getName() + "," + a.getDog().getAge());
        Animal a2 = (Animal) a.clone();
        a2.setAge(13);
        a2.setName("Xu Chun Jie");
        a2.getDog().setAge(14);
        a2.getDog().setName("Li Wang Hong");
        System.out.println(a.getAge() + "," + a.getName() + "," + a.getDog().getName() + "," + a.getDog().getAge());
        System.out.println(a2.getAge() + "," + a2.getName() + "," + a2.getDog().getName() + "," + a2.getDog().getAge());
    }

}

class Animal implements Cloneable{
    private int age;
    private String name;
    private Dog dog;
    public Animal(){}

    public int getAge() {
        return age;
    }
    public void setAge(int age) {
        this.age = age;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }


    public Animal(int age, String name, Dog dog) {
        super();
        this.age = age;
        this.name = name;
        this.dog = dog;
    }

    public Dog getDog() {
        return dog;
    }

    public void setDog(Dog dog) {
        this.dog = dog;
    }

    public Object clone() throws CloneNotSupportedException{
        return super.clone();
    }
}

class Dog implements Cloneable{
    private int age;
    private String name;
    public int getAge() {
        return age;
    }
    public void setAge(int age) {
        this.age = age;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public Dog(int age, String name) {
        super();
        this.age = age;
        this.name = name;
    }

}

Output:
11, Yan Haibin, Liu Zecheng, 10
11, Yan Haibin, Li Wanghong, 14
13, Xu Chunjie, Li Wanghong, 14

2. Deep Reproduction

package other;

//Deep replication
public class test4 {
    public static void main(String[] args) {
        B b = new B(10, "Xu Chun Jie");
        A a = new A(11, "Li Wang Hong", b);
        System.out.println(a.getAge()+","+a.getName()+","+a.getB().getName()+","+a.getB().getAge());
        A a1 = (A)a.clone();
        a1.setAge(12);
        a1.setName("Xu Bi Xiao");
        a1.getB().setAge(13);
        a1.getB().setName("Yan Hai bin");
        System.out.println(a.getAge()+","+a.getName()+","+a.getB().getName()+","+a.getB().getAge());
        System.out.println(a1.getAge()+","+a1.getName()+","+a1.getB().getName()+","+a1.getB().getAge());
    }
}

class A implements Cloneable{
    private int age;
    private String name;
    private B b;
    public A(int age, String name, B b) {
        super();
        this.age = age;
        this.name = name;
        this.b = b;
    }
    public int getAge() {
        return age;
    }
    public void setAge(int age) {
        this.age = age;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public B getB() {
        return b;
    }
    public void setB(B b) {
        this.b = b;
    }

    public Object clone(){
        A o = null;
        try{
            o = (A)super.clone();
        }catch(Exception e){
            e.printStackTrace();
        }
        o.b = (B)b.clone();
        return o;
    }
}

class B implements Cloneable{
    private int age;
    private String name;
    public B(int age, String name) {
        super();
        this.age = age;
        this.name = name;
    }
    public int getAge() {
        return age;
    }
    public void setAge(int age) {
        this.age = age;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }

    public Object clone(){
        Object o = null;
        try{
            o = super.clone();
        }catch(Exception e){
            e.printStackTrace();
        }
        return o;
    }
}

Output:
11, Li Wanghong, Xu Chunjie, 10
11, Li Wanghong, Xu Chunjie, 10
12, Xu Bixiao, Yan Haibin, 13

3. Deep Reproduction by Serialization
Writing objects into streams is a serialization process, while reading objects out of streams is a design process. It should be noted that what is written in the stream is a copy of the object, while the original object still exists in the JVM.

In the Java language, when an object is copied in depth, it is often possible to first implement the Serializable interface for the object, then write the object (actually only a copy of the object) into a stream, and then read it out of the stream, so that the object can be reconstructed.

This is done on the premise that the object and all referenced objects within the object are serializable. Otherwise, it is necessary to carefully examine whether the non-serializable objects are set to transient, thus excluding them from the replication process. The code is improved as follows:

package other;

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

//Using serialization to achieve deep object replication, the premise is that the object and all referenced objects inside the object are serializable.
public class test5 {
    public static void main(String[] args) throws Exception {
        D d = new D(10, "Liu Cheng Cheng");
        C c = new C(11, "Xu Chun Jie", d);
        System.out.println(c.getAge()+","+c.getName()+","+c.getD().getAge()+","+c.getD().getName());
        C c1 = (C)c.deepClone();
        c1.setAge(12);
        c1.setName("Yan Hai bin");
        c1.getD().setAge(13);
        c1.getD().setName("Li Wang Hong");
        System.out.println(c.getAge()+","+c.getName()+","+c.getD().getAge()+","+c.getD().getName());
        System.out.println(c1.getAge()+","+c1.getName()+","+c1.getD().getAge()+","+c1.getD().getName());
    }
}

class C implements Serializable{
    private int age;
    private String name;
    private D d;
    public C(){}

    public C(int age, String name, D d) {
        super();
        this.age = age;
        this.name = name;
        this.d = d;
    }

    public D getD() {
        return d;
    }

    public void setD(D d) {
        this.d = d;
    }

    public int getAge() {
        return age;
    }
    public void setAge(int age) {
        this.age = age;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }

    //Deep replication
    public Object deepClone() throws Exception{
        //Write objects into streams
        ByteArrayOutputStream bo = new ByteArrayOutputStream();
        ObjectOutputStream oo = new ObjectOutputStream(bo);
        oo.writeObject(this);

        //Read from Stream
        ByteArrayInputStream bi = new ByteArrayInputStream(bo.toByteArray());
        ObjectInputStream oi = new ObjectInputStream(bi);
        return oi.readObject();
    }

}

class D implements Serializable{
    private int age;
    private String name;
    public D(){}
    public D(int age, String name) {
        super();
        this.age = age;
        this.name = name;
    }
    public int getAge() {
        return age;
    }
    public void setAge(int age) {
        this.age = age;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
}

Output:
11, Xu Chunjie, 10, Liu Zecheng
11, Xu Chunjie, 10, Liu Zecheng
12, Yan Haibin, 13, Li Wanghong

Keywords: Java jvm

Added by vtbruinsfan on Tue, 21 May 2019 00:07:43 +0300