Introduction to the

Before we look at the prototype pattern, let’s understand that Java provides two types of cloning:

  • Shallow copy: All variables of the cloned object have the same values as the original object, and all references to other objects still refer to the original object. To put it another way, shallow cloning clones only the object in question, not the object it references.
  • Deep copy: All variables of the cloned object have the same values as the original object, but all references to other objects are no longer original, which refer to the new object that has been copied. In other words, deep copy copies all references to the object to be copied. This is called indirect copy.

Interview question: What is the difference between shallow copy and deep copy?

  • Shallow copy is a copy of basic data type and reference data type. (8 kinds of basic data type byte, char, short, int, long, float, double, Boolean)

  • Deep copy passes values to the base data type, creates a new object for the reference data type, and copies its contents. (Except for the 8 basic data types in the shallow copy, all the others are in the deep copy, such as arrays, objects…)

Prototype patterns are used to create repetitive objects while maintaining performance. Specify what kind of objects to create with prototype instances, and create new objects by copying these prototypes.

Code sample

1. The shallow copy

public class IdCard {
    private String id;

    public IdCard(String id) {
        this.id = id;
    }

    @Override
    public String toString(a) {
        return "IdCard{" +
                "id=" + id +
                '} '; }}Copy the code
public class Person implements Cloneable {

    private String name;
    private int age;
    private IdCard idCard;

    public Person(a) {}public Person(String name, int age, IdCard idCard) {
        this.name = name;
        this.age = age;
        this.idCard = idCard;
    }

    public String getName(a) {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge(a) {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public IdCard getIdCard(a) {
        return idCard;
    }

    public void setIdCard(IdCard idCard) {
        this.idCard = idCard;
    }

    @Override
    public String toString(a) {
        return "Person{" +
                "name='" + name +
                ", age=" + age +
                ", idCard=" + idCard + 
                ", idCard.hashCode=" + idCard.hashCode() +
                '} ';
    }

    @Override
    protected Object clone(a) throws CloneNotSupportedException {
        return super.clone(); }}Copy the code

The execution result

public class PersonTest {
    public static void main(String[] args) throws Exception {
        Person person = new Person("Zhang".20.new IdCard("10086")); Person person1 = (Person) person.clone(); Person person2 = (Person) person.clone(); System.out.println(person); System.out.println(person1); System.out.println(person2); }}Copy the code
Person{name='Person{name=', age=20, idCard= idCard {id=10086}, idcard. hashCode=1510467688}Zhang SAN, age =20, idCard=IdCard{id=10086}, idCard.hashCode=1510467688}
Person{name=IdCard{id=10086}, idcard. hashCode=1510467688}Copy the code

We can find by implementing implements Cloneable to complete a shallow copy, basic variable is the value transfer, cloning and the reference object IdCard is reference, it is not about our object-oriented thought, every Person should have an independent IdCard, rather than sharing a, To solve this problem, we need to use deep cloning

2. Deep copy (type 1)

// Implement the Cloneable interface
public class IdCard implements Cloneable {
    private String id;

    public IdCard(String id) {
        this.id = id;
    }

    public String getId(a) {
        return id;
    }

    public void setId(String id) {
        this.id = id;
    }

    @Override
    public String toString(a) {
        return "IdCard{" +
                "id='" + id + 
                '} ';
    }

    @Override
    protected Object clone(a) throws CloneNotSupportedException {
        return super.clone(); }}Copy the code
public class Person implements Cloneable {
    private String name;
    private int age;
    private IdCard idCard;

    public Person(String name, int age, IdCard idCard) {
        this.name = name;
        this.age = age;
        this.idCard = idCard;
    }

    public IdCard getIdCard(a) {
        return idCard;
    }

    public void setIdCard(IdCard idCard) {
        this.idCard = idCard;
    }

    @Override
    public String toString(a) {
        return "Person{" +
                "personHashCode=" + this.hashCode() +
                ", name='" + name + 
                ", age=" + age +
                ", idCard=" + idCard +
                ", idCardHashCode=" + idCard.hashCode() +
                '} ';
    }

    // Deep cloning needs to be implemented manually, as well as in object references
    @Override
    protected Object clone(a) throws CloneNotSupportedException {
        // The basic data type is copied
        // Objects created with the new keyword are reference types

        Object person = super.clone();

        // The reference type is treated separately
        Person p = (Person) person;
        IdCard idCard = (IdCard) p.getIdCard().clone(); // Create your own clone
        p.setIdCard(idCard);
        returnp; }}Copy the code

The execution result

public class PersonTest implements Serializable {
    public static void main(String[] args) throws Exception {

        Person person = new Person("Zhang".20.new IdCard("10086"));

        Person person1 = (Person) person.clone();
        Person person2 = (Person) person.clone();

        System.out.println(person);
        System.out.println(person1);
        System.out.println(person2);

    }
}

Person{personHashCode=1510467688, name='age=20, idCard= idCard {id='10086}, idCardHashCode=1995265320}
Person{personHashCode=746292446, name='age=20, idCard= idCard {id='10086}, idCardHashCode=1072591677}
Person{personHashCode=1523554304, name='age=20, idCard= idCard {id='10086}, idCardHashCode=1175962212}

Copy the code

Using this method of deep cloning, the perfect solution when the data type to a reference type, only copy of the original reference object address instead of a new reference object references, but this implementation has the disadvantages of a large, need to clone method in each object, if the class is full of your own writing, the natural right, to implement the line, But it’s a bit of a hassle. However, if you are referring to a third party class and cannot modify the source code, it is obviously not possible to implement deep cloning in this way

3. Deep copy (second type)

// Implement Serializable interface
public class IdCard implements Serializable {
    private String id;

    public IdCard(String id) {
        this.id = id;
    }

    public String getId(a) {
        return id;
    }

    public void setId(String id) {
        this.id = id;
    }

    @Override
    public String toString(a) {
        return "IdCard{" +
                "id='" + id + 
                '} ';
    }

    @Override
    protected Object clone(a) throws CloneNotSupportedException {
        return super.clone(); }}Copy the code
public class Person implements Serializable {
    private String name;
    private int age;
    private IdCard idCard;

    public Person(String name, int age, IdCard idCard) {
        this.name = name;
        this.age = age;
        this.idCard = idCard;
    }

    public IdCard getIdCard(a) {
        return idCard;
    }

    public void setIdCard(IdCard idCard) {
        this.idCard = idCard;
    }

    @Override
    public String toString(a) {
        return "Person{" +
                "personHashCode=" + this.hashCode() +
                ", name='" + name + 
                ", age=" + age +
                ", idCard=" + idCard +
                ", idCardHashCode=" + idCard.hashCode() +
                '} ';
    }

    // Serialization
    public Person deelClone(a) {
        // Create a stream object
        ByteArrayOutputStream bos = null;
        ObjectOutputStream oos = null;
        ByteArrayInputStream bis = null;
        ObjectInputStream ois = null;

        try {
            / / the serialization
            bos = new ByteArrayOutputStream();
            oos = new ObjectOutputStream(bos);
            oos.writeObject(this);

            // deserialize
            bis = new ByteArrayInputStream(bos.toByteArray());
            ois = new ObjectInputStream(bis);
            return (Person) ois.readObject();
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        } finally {
            try {
                ois.close();
                bis.close();
                oos.close();
                bos.close();
            } catch(IOException e) { e.printStackTrace(); }}}}Copy the code

The execution result

public class PersonTest {
    public static void main(String[] args) throws Exception {
        // Create an object
        Person person = new Person("Zhang".20.new IdCard("10086"));

        // Clone two objects
        Person person1 = (Person) person.deelClone();
        Person person2 = (Person) person.deelClone();
        System.out.println("Deep copy (second implementation of serialization interface)");
        // Prints information about three peopleSystem.out.println(person); System.out.println(person1); System.out.println(person2); }} deep copy (second implementation of serialization interface) Person{personHashCode=2055281021, name='age=20, idCard= idCard {id='10086}, idCardHashCode=1554547125}
Person{personHashCode=804564176, name='age=20, idCard= idCard {id='10086}, idCardHashCode=1421795058}
Person{personHashCode=1555009629, name='age=20, idCard= idCard {id='10086}, idCardHashCode=41359092}

Copy the code

In this way, we need to manually write the deepClone method, which uses serialization and deserialization in Java streams to implement deep cloning. However, this implementation requires that the Serializable interface be inherited from each class. This way, if you call a third party class, It is also possible that the Serializable interface is not implemented on third-party classes, but in general, most of them are implemented. In general, this comparison is recommended and efficient

4. Summary of prototype model

Archetypal patterns are essentially object copies. Using the prototype pattern can solve the resource consumption problem of building complex objects, can improve the efficiency of creating objects in some scenarios, and also has the feature of protective copy, if we operate, will not affect the original object.

Advantages: Prototype mode is a copy of the binary stream in memory, which is better than new an object, especially if you need to produce a large number of objects.

Disadvantages: copy directly in memory, constructors are not executed, this reduces constraints, both advantages and disadvantages, in real development should pay attention to this issue.