If you don't want to write objects, clone one.

Posted by homie on Sun, 07 Jul 2019 02:18:05 +0200

1. Why cloning?

For example, sometimes we need to get a very complex object. Instead of using new to create objects and assign values to each attribute, we can clone existing objects directly. In java, "=" can be used to clone base types, but not reference types.

Why not use "=" directly for reference types?

Answer: Because java divides memory space into two blocks, the stack and stack. Variables of base type and reference type are saved in the stack and objects are saved in the heap. If "=" is used, the reference will be modified. At this point, two reference variables point to the same object, and if one of them is modified, the other variable will be changed.

Examples are given to illustrate:
Baldheaded Strong - > Bank Card < - Grey Wolf

Baldheaded and Grey Wolf share a bank card. Whoever deposits money in it will have an impact on the other party.

2. The wrong way of cloning (pseudocloning)

package test;

public class Test {

    public static void main(String[] args) {

        Person p = new Person("Lazy",10);
        System.out.println("Preclonal Person");
        System.out.println(p.toString());
        Person pClone = p;
        //Reassigning name and age
        pClone.name = "Strong bald head";
        pClone.age = 80;
        System.out.println("Cloned Person");
        System.out.println(pClone.toString());
        System.out.println("pClone After modifying attributes, output p Information");
        System.out.println(p.toString());

    }

}

Test results:

Person before cloning
 Name: Lazy Age: 10
 Cloned Person
 Name: Baldheaded Age: 80
 After pClone modifies the attributes, it outputs the information of p
 Name: Baldheaded Age: 80

Other knowledge points: The knowledge points in the stack will be recycled immediately after they are used, and the objects in the heap are managed by the java virtual machine. Even if the object is no longer used, the memory space will only be recycled at an uncertain time.

3. Shallow Cloning of Java Objects

  1. Why is it called shallow cloning?

    Answer: Because replication of basic types of domains (attributes or methods) is effective, errors may occur for reference types of domains. But it avoids the problem of "one reference variable being modified to affect another".

  2. Interfaces to be implemented

    Answer: You need to implement the Cloneable interface. If you don't implement this interface, the clone() method throws a CloneNotSupportedException exception.

Code testing:

 package test;
/**
 * Create a dog class
 */
public class Dog {

    public String dogName;
    public String dogBreed;//Breed of dog

    public Dog(String dogName, String dogType) {
        this.dogName = dogName;
        this.dogBreed = dogType;
    }

    public String toString(){
        StringBuffer sb = new StringBuffer();
        sb.append("Name of dog:"+dogName+",")
          .append("Dog breeds:"+dogBreed);
        return sb.toString();
    }
}

Create a human being:

package test;

/**
 * Creating a Human Being
 * @author hgx
 *
 */
public class Person implements Cloneable{

    public String name;
    public int age;
    Dog dog = null;

    public Person(){

    }

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

    //Implementing Cloneable interface and rewriting clone() method
    public Person clone(){
        Person p = null;
        try {
            p = (Person) super.clone();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return p;
    }

    //Each class has a toString() method that we can override
    public String toString(){
        StringBuffer sb = new StringBuffer();
        sb.append("Man's Name:"+name+",")
          .append("Human age:"+age+",")
          .append("Owned dogs:"+dog);
        return sb.toString();
    }        
}

Test class:

 package test;

public class Test {

    public static void main(String[] args) {
        Dog dog = new Dog("Himmies", "Siberian Husky");
        Person p = new Person("Lazy", 10, dog);
        System.out.println("Preclonal Person");
        System.out.println(p.toString());
        Person pClone = p.clone();
        // Reassigning name and age
        pClone.name = "Strong bald head";
        //Modify the dog
        pClone.dog.dogName = "Tibetan Mastiff";
        System.out.println("Cloned Person");
        System.out.println(pClone.toString());
        System.out.println("pClone After modifying attributes, output p Information");
        System.out.println(p.toString());

    }

}

Test results:

Person before cloning
 Man's Name: Lazy, Man's Age: 10, Owned Dog: Dog's Name: Himmy, Dog's Breed: Husky
 Cloned Person
 Man's Name: Baldheaded, Man's Age: 10, Dog: Dog's Name: Tibetan Mastiff, Dog's Breed: Husky
 After pClone modifies the attributes, it outputs the information of p
 Man's Name: Lazy, Man's Age: 10, Owned Dog: Dog's Name: Tibetan Mastiff, Dog's Breed: Husky

Summary: We can find that although the cloned pClone modifies the name and age attributes, it has no effect on the original P attributes. But when we modify the reference to dog, the original p's dog also changes. This also shows that "shallow cloning is a piece of cake for replication of basic types, but it can not completely control the reference type unless we do not modify the reference type".
This is the problem of shallow cloning. But there are policies and countermeasures. When it comes to deep cloning, these problems will all die.

Other knowledge points involved: StringBuffer, toString() method

4. Deep Cloning of Java

  1. Why is it called deep cloning?

    We already know this question. If you don't say too much, you will lose too much. Look at the code.
    Since most of the code is the same, only the changes are written below.

Code case:

1. Change of Dog Class: Implement Cloneable Interface and Rewrite clone() Method

//Be careful not to forget to implement Cloneable
@Override
protected Dog clone(){
    Dog dog = null;
    try {
        dog = (Dog) super.clone();
    } catch (CloneNotSupportedException e) {
        e.printStackTrace();
    }
    return dog;
}

2. Change of Person class: clone() method is added with clone of Dog class

@Override
    public Person clone(){
        Person p = null;
        try {
            p = (Person) super.clone();
            p.dog = dog.clone();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return p;
    }

Test results:

Person before cloning
 Man's Name: Lazy, Man's Age: 10, Owned Dog: Dog's Name: Himmy, Dog's Breed: Husky
 Cloned Person
 Man's Name: Baldheaded, Man's Age: 10, Dog: Dog's Name: Tibetan Mastiff, Dog's Breed: Husky
 After pClone modifies the attributes, it outputs the information of p
 Man's Name: Lazy, Man's Age: 10, Owned Dog: Dog's Name: Himmy, Dog's Breed: Husky

Summary: The results have proved that our goal has been achieved. Changing a clone at random will not affect the original class.

Note: Usually, deep cloning is used when cloning objects, but it should be noted that if there are references in the reference type, Cloneable interface needs to be implemented to rewrite the clone() method.

5. Differences between deep cloning and shallow cloning

  1. Code differences: Shallow cloning implements Cloneable interface only for cloning class, while deep cloning implements Cloneable interface not only for cloning class, but also for reference type in cloning class.

  2. Functionally different, deep cloning solves the problem that shallow cloning can't replicate reference types very well.

    6. Sequencing and Object Cloning

1. Why should we use serialization to clone objects?

If there are many types of classes, and there are reference types in the reference type, then don't we have to override the clone() method every time, which is too troublesome. So serialization is the solution to this problem.

2. What is serialization?

Serialization, in short, is like a time funnel, where sand falls continuously until the funnel is over. In java, serialized objects copy one object to another bit by bit. Actually, it looks like a 3D printer.

3. General process

Using the output stream, the object is written to an array of bytes in byte form, and then read from the array of bytes through the input stream. There is no need to consider the type of reference at this point. Because serialization can be said to start from the root cause and essentially achieve cloning. Just like a leaky pipe, it's no use just cleaning up the water. It's only possible to close the pipe.

Life Perception: So the problem should be solved from the root, in order to ensure that nothing is lost. If middle-aged java encounters some problems, we can think about how to solve them in life.

4. Interfaces to be implemented

Serializable interface

5. Example code

package test;
import java.io.Serializable;
// Create a dog class
public class Dog implements Serializable{

    public String dogName;
    public String dogBreed;//Breed of dog

    public Dog(String dogName, String dogType) {
        this.dogName = dogName;
        this.dogBreed = dogType;
    }

    public String toString(){
        StringBuffer sb = new StringBuffer();
        sb.append("Name of dog:"+dogName+",")
          .append("Dog breeds:"+dogBreed);
        return sb.toString();
    }    
}

// Human beings

package test;
/**
 * Creating a Human Being
 */
public class Person implements Cloneable, Serializable {

    public String name;
    public int age;
    Dog dog = null;

    public Person() {

    }

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

    @Override
    public Person clone() {
        Person p = null;
        ByteArrayOutputStream bao = new ByteArrayOutputStream();
        try {
            ObjectOutputStream oos = new ObjectOutputStream(bao);
            oos.writeObject(this);
            oos.close();
        } catch (IOException e) {

            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        ByteArrayInputStream bis = new ByteArrayInputStream(bao.toByteArray());
        try {
            ObjectInputStream ois = new ObjectInputStream(bis);
            p = (Person) ois.readObject();
            ois.close();
        } catch (IOException | ClassNotFoundException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        return p;
    }

    public String toString() {
        StringBuffer sb = new StringBuffer();
        sb.append("Man's Name:" + name + ",").append("Human age:" + age + ",").append("Owned dogs:" + dog);
        return sb.toString();
    }

}

The test class remains unchanged:
Test results:

Person before cloning
 Man's Name: Lazy, Man's Age: 10, Owned Dog: Dog's Name: Himmy, Dog's Breed: Husky
 Cloned Person
 Man's Name: Baldheaded, Man's Age: 10, Dog: Dog's Name: Tibetan Mastiff, Dog's Breed: Husky
 After pClone modifies the attributes, it outputs the information of p
 Man's Name: Lazy, Man's Age: 10, Owned Dog: Dog's Name: Himmy, Dog's Breed: Husky

Note: Serialized objects require the implementation of Cloneable and Serializable interfaces. If there are reference types in the class, the reference types also need to implement Serializable interfaces. In terms of efficiency, serialization is slower than deep cloning.

7. Choosing the appropriate cloning method

Shallow cloning can be used if the basic type and reference type of each attribute of the class do not change. Otherwise, use deep cloning. If the structure of the class is complex, serialization is used.

8. Application of Transient Keyword

1. Why use the transient keyword?

For example, there are some attributes. If we don't want to clone a new object to access its value (such as password), we can add the transient keyword to the front of the attribute. When cloning this way, although the variable is cloned into a new object, its value is null (if it is a reference type) or 0 (if it is a basic type).

2.transient Notes

1. transient can only be used to modify attributes, not methods.
2. If you use serialization of a class and the reference type in that class does not implement the Serializable interface or do not want to serialize it, you need to modify the reference type with transient s.

Topics: Java Attribute