Cloning and serializing applications

Clone

Before we start to learn about cloning, let's look at the following code. What's the problem with normal object replication?

class CloneTest {
    public static void main(String[] args) throws CloneNotSupportedException {
        // Assignment of equal sign (basic type)
        int number = 6;
        int number2 = number;
        // Modify the value of number2
        number2 = 9;
        System.out.println("number: " + number);
        System.out.println("number2: " + number2);
        // Equal sign assignment (object)
        Dog dog = new Dog();
        dog.name = "Prosperous wealth";
        dog.age = 5;
        Dog dog2 = dog;
        // Modify the value of dog2
        dog2.name = "Chinese rhubarb";
        dog2.age = 3;
        System.out.println(dog.name + "," + dog.age + "year");
        System.out.println(dog2.name + "," + dog2.age + "year");
    }
}

Program execution result:

number: 6
number2: 9
 Rhubarb, 3 years old
 Rhubarb, 3 years old

It can be seen that when using the equal sign to copy, the modification operations between value types are relatively independent, while for reference types, because the memory address of the reference object is copied, modifying one of the values will change with the other, as shown in the following figure

Therefore, in order to prevent this problem, we need to use object cloning to solve the problem of reference type replication.

1) Shallow clone

The default clone() method is shallow clone, and the code is as follows:

class CloneTest {
    public static void main(String[] args) throws CloneNotSupportedException {
        Dog dog = new Dog();
        dog.name = "Prosperous wealth";
        dog.age = 5;
        // Clone
        Dog dog3 = (Dog) dog.clone();
        dog3.name = "Xiao Bai";
        dog3.age = 2;
        System.out.println(dog.name + "," + dog.age + "year");
        System.out.println(dog3.name + "," + dog3.age + "year");
    }
}
class Dog implements Cloneable {
    public String name;
    public int age;
    @Override
    protected Object clone() throws CloneNotSupportedException {
        return super.clone();
    }
}

Program execution result:

Wangcai, 5 years old
 Xiaobai, 2 years old

It can be seen that using clone can solve the problem of reference type duplication. The principle is shown in the following figure:

The above method of replication is called shallow cloning.

The implementation condition of shallow clone: the object to be cloned must implement the clonable interface, and override the clone() method to clone this object.

However, there is also a problem with shallow cloning. Please refer to the following code.

class CloneTest {
    public static void main(String[] args) throws CloneNotSupportedException {
        DogChild dogChild = new DogChild();
        dogChild.name = "Two dogs";
        Dog dog4 = new Dog();
        dog4.name = "Chinese rhubarb";
        dog4.dogChild = dogChild;
        Dog dog5 = (Dog) dog4.clone();
        dog5.name = "Prosperous wealth";
        dog5.dogChild.name = "Dog two";
        System.out.println("dog name 4: "+dog4.name);
        System.out.println("dog name 5: "+dog5.name);
        System.out.println("dog child name 4: "+dog4.dogChild.name);
        System.out.println("dog child name 5: "+dog5.dogChild.name);
    }
}
class Dog implements Cloneable {
    public String name;
    public DogChild dogChild;
    @Override
    protected Object clone() throws CloneNotSupportedException {
        return super.clone();
    }
}
class DogChild {
    public String name;
}

Program execution result:

dog name 4: Rhubarb
 dog name 5: Wangcai
 dog child name 4: dog two
 dog child name 5: dog two

In other words, shallow cloning only copies the value type of the object, not the reference type of the object. The reasons are as follows:

To deal with the problem that reference types are not copied, you need to use deep cloning.

2) Deep clone

Definition: deep clone is to copy the whole object information, including value type and reference type.

Deep cloning is usually implemented in two ways.

  • Serialization to achieve deep cloning: first serialize the original object into the byte stream of memory, and then deserialize the object just stored from the byte stream. There is no address sharing between the new object and the original object, so deep cloning is realized.
  • All reference types should be cloned: all reference types of objects to be copied should be cloned, and all objects are new objects to be copied, thus realizing deep cloning.

Deep clone implementation mode 1: serialization

Implementation idea: first write the object to be copied into the byte stream in memory, then read out the information just stored from the byte stream, and return it as a new object. Then there is no address sharing between the new object and the original object, which naturally realizes deep copying. Refer to the following code:

class CloneTest {
    public static void main(String[] args) throws CloneNotSupportedException {
        BirdChild birdChild = new BirdChild();
        birdChild.name = "Little bird";
        Bird bird = new Bird();
        bird.name = "Little bird";
        bird.birdChild = birdChild;
        // Cloning objects using serialization
        Bird bird2 = CloneUtils.clone(bird);
        bird2.name = "Yellow bird";
        bird2.birdChild.name = "Little yellow Finch";
        System.out.println("bird name:" + bird.name);
        System.out.println("bird child name:" + bird.birdChild.name);
        System.out.println("bird name 2:" + bird2.name);
        System.out.println("bird child name 2:" + bird2.birdChild.name);
    }
}
class CloneUtils {
    public static <T extends Serializable> T clone(T obj) {
        T cloneObj = null;
        try {
            //Write byte stream
            ByteArrayOutputStream bo = new ByteArrayOutputStream();
            ObjectOutputStream oos = new ObjectOutputStream(bo);
            oos.writeObject(obj);
            oos.close();
            //Allocate memory, write original object, generate new object
            ByteArrayInputStream bi = new ByteArrayInputStream(bo.toByteArray());//Get the output byte stream above
            ObjectInputStream oi = new ObjectInputStream(bi);
            //Return the generated new object
            cloneObj = (T) oi.readObject();
            oi.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return cloneObj;
    }
}

Program execution result:

bird name: Bird
 bird child name: little bird
 bird name 2: Yellow Finch
 bird child name 2: little yellow Finch

Deep clone implementation mode 2: all reference types implement cloning

class SerializableTest {
    public static void main(String[] args) throws IOException, ClassNotFoundException {
    ParrotChild parrotChild = new ParrotChild();
        parrotChild.name = "Small parrot";
        Parrot parrot = new Parrot();
        parrot.name = "Macaw ";
        parrot.parrotChild = parrotChild;
        // Clone
        Parrot parrot2 = (Parrot) parrot.clone();
        parrot2.name = "Old Parrot";
        parrot2.parrotChild.name = "Little parrot";
        System.out.println("parrot name:" + parrot.name);
        System.out.println("parrot child name:" + parrot.parrotChild.name);
        System.out.println("parrot name 2:" + parrot2.name);
        System.out.println("parrot child name 2:" + parrot2.parrotChild.name);
    }
 }
class Parrot implements Cloneable {
    public String name;
    public ParrotChild parrotChild;
    @Override
    protected Object clone() throws CloneNotSupportedException {
        Parrot bird = (Parrot) super.clone();
        bird.parrotChild = (ParrotChild) parrotChild.clone();
        return bird;
    }
}
class ParrotChild implements Cloneable {
    public String name;
    @Override
    protected Object clone() throws CloneNotSupportedException {
        return super.clone();
    }
}

Program execution result:

parrot name: parrot
 parrot child name: parrot
 parrot name 2: old Parrot
 parrot child name 2: Little Parrot

serialization and deserialization

1) Introduction

Data objects in memory can only be persisted or transmitted on the network when they are converted into binary streams. The process of converting objects into binary streams is called Serialization. On the contrary, the process of restoring binary streams to data objects is called Deserialization.

2) Serialization and deserialization code implementation

First serialize the object to disk, and then deserialize the object from disk. Please refer to the following code:

class SerializableTest {
    public static void main(String[] args) throws IOException, ClassNotFoundException {
        // Object Assignment 
        User user = new User();
        user.setName("Lao Wang");
        user.setAge(30);
        System.out.println(user);
        // Create output stream (serialize content to disk)
        ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("test.out"));
        // serialized objects 
        oos.writeObject(user);
        oos.flush();
        oos.close();
        // Create input stream (deserialize from disk)
        ObjectInputStream ois = new ObjectInputStream(new FileInputStream("test.out"));
        // Deserialization
        User user2 = (User) ois.readObject();
        ois.close();
        System.out.println(user2);
    }
}
class User implements Serializable {
    private static final long serialVersionUID = 3831264392873197003L;
    private String name;
    private int age;
    @Override
    public String toString() {
        return "{name:" + name + ",age:" + age + "}";
    }
    // setter/getter...
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public int getAge() {
        return age;
    }
    public void setAge(int age) {
        this.age = age;
    }
}

Program execution result:

{name: Lao Wang, age:30}
{name: Lao Wang, age:30}

For more details on how to implement serialization and deserialization, as well as code examples, see the interview section below.

Developer settings: IDEA enables automatic generation of serialVersionUID Click Settings → Inspections → search Serialization issues → check Serializable class without 'SerialVersionUID' to save the Settings, as shown in the following figure:

After setting, place the cursor on the class name and click the prompt to generate the serialVersionUID, as shown in the following figure:

Tags: Programming network

Posted on Tue, 05 May 2020 23:53:26 -0400 by cl77