preface

This article is a complement to my previous blog on Java object cloning, which explicitly clones references in the data domain of the cloned object. In other domains, the clone method is also used one by one, and finally completes the deep cloning of objects. This article uses a serialized approach to object cloning, as seen in Chapter 2 of Java Core Technologies Volume 2.

Serialized clone

Definition of related classes

// The object data field of the Person class, used to check whether a deep clone has been implemented
class Pet implements Cloneable ,Serializable{
    String type;
    public Pet(String type) {
        this.type = type;
    }
    public void setType(String type) {
        this.type = type;
    }
    @Override
    public String toString(){
        return this.type; }}// The Person class, which is used for cloning
class Person implements Cloneable.Serializable{
    String name;
    int age;
    Pet pet;
    public Person(String name, int age, Pet pet){
        this.name = name;
        this.age = age;
        this.pet = pet;
    }
    public void setPet(Pet pet){
        this.pet = pet;
    }
    public Pet getPet(){
        return pet;
    }
    public void setAge(int age){
        this.age = age;
    }
    public void setName(String name) {
        this.name = name;
    }
    @Override
    // Clone with serialization
    public Person clone() throws CloneNotSupportedException {
        // many lines
    }
    @Override
    public String toString(){
        return "name:"+name + " age:" + age + " pet:"+ pet; }}Copy the code

First of all, let’s look at the definition of cloning-related classes. Like Java object cloning, a Pet class is defined as a member of the data domain of the Person class, so as to detect whether a deep clone is successfully carried out by changing the value of the Pet data domain of the cloned object. The Pet and Person classes both have set and GET methods, as well as overriding toString methods. In addition, both classes implement the Serializable interface, which is also a tag interface that must be implemented to serialize objects.

Serialized clone method

public Person clone() throws CloneNotSupportedException {
    try {
        ByteArrayOutputStream bout = new ByteArrayOutputStream();
        try (ObjectOutputStream out = new ObjectOutputStream(bout))
        {
            out.writeObject(this);
        }
        try (InputStream bin = new ByteArrayInputStream(bout.toByteArray()))
        {
            ObjectInputStream in = new ObjectInputStream(bin);
            return (Person)in.readObject(); }}catch (IOException | ClassNotFoundException e) {
        e.printStackTrace();
    }
    return null;
}
Copy the code

In this section, you can see that ObjectOutputStream is used to write the object into ByteArrayOutputStream. Then read from ByteArrayOutputStream with ObjectInputStream to implement deserialization, so using serialization and deserialization ideas successfully clone. Because there is no object transfer or persistence involved, just cloning, there is no serialization to disk.

Tests whether the clone is a hyperclone

    public static void main(String[] args) throws CloneNotSupportedException{
        Person per = new Person("testClone".18.new Pet("cat"));
        Person perClone = per.clone();
        // Set the data domains of the cloned object
        perClone.setAge(19);
        perClone.setName("clone");
        perClone.getPet().setType("dog");
        // Outputs two objects
        System.out.println("Original object:"+per);
        System.out.println("Clone object:"+perClone);
    }
Copy the code

Modify each data domain of the cloned object, and test it with the same method in Java object cloning. The results are as follows:

The modification of the cloned object does not affect the original object, and this method also completes the deep cloning.

The efficiency of detection

As mentioned in Java Core Technology Volume 2, this serialized clone method is much slower than the previous method of copying data fields one by one, so let’s test it out.

    public static void main(String[] args) throws CloneNotSupportedException{
        Person per = new Person("testClone".18.new Pet("cat"));
        long startTime=System.currentTimeMillis();
        for (int i = 0; i <10000; ++i) { Person perClone = per.clone(); } long endTime=System.currentTimeMillis(); System.out.println("Running time:" + (endTime - startTime) + "ms");
        
    }
Copy the code

The main function of the test is as above, to perform ten thousand object clones, we first test the serialized clone time:

The clone method is then commented out and replaced with the following traditional method code:

public Person clone() throws CloneNotSupportedException{
    Person cloned = (Person)super.clone();
    cloned.pet = pet.clone();
    return cloned;
}
Copy the code

Run the same program, test it, and the results are as follows:

This is a hundredfold difference in efficiency, presumably because serialized writes and reads of input and output streams are more time-consuming than direct cloning.

To test my guess and satisfy my curiosity, I rewrote the serialized clone method as follows:

public Person clone() throws CloneNotSupportedException {
        try {
            try(ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("testClone.txt")))
            {
                out.writeObject(this);
            }
            try(ObjectInputStream in = new ObjectInputStream(new FileInputStream("testClone.txt")))
            {
                return (Person)in.readObject(); }}catch (IOException | ClassNotFoundException e) {
            e.printStackTrace();
        }
        return null;
    }
Copy the code

Now instead of writing and reading from ByteArrayOutputStream in memory, serialize the object to testClone. TXT on disk, deserialize it back, run the same amount of time, and the result is as follows:

You can see that efficiency is very touching. Both serializations and deserializations are two hundred times slower than ByteArrayOutputStream.

conclusion

Java’s object serialization and deserialization provide another way to clone objects. The advantage of this method is that it is easy to write code and can carry out deep copy without rewriting the Clone method by picking out the object reference of the data field separately. The disadvantage is efficiency is more touching.