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 oldIt 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 cloneThe 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 oldIt 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 twoIn 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 cloneDefinition: 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 FinchDeep 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 Parrotserialization and deserialization
1) IntroductionData 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 implementationFirst 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 ""; } // 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:
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: