The problem of cloning sheep

There is a sheep Tom, name: Tom, age: 1, color: white, please write a program to create 10 sheep with the same attributes as Tom sheep. Common solution

@Data public class Sheep { private String name; private int age; private String color; public Sheep(String name, int age, String color) { this.name = name; this.age = age; this.color = color; } @Override public String toString() { return "Sheep{" + "name='" + name + ''' + ", age=" + age + ", color='" + color + ''' + '}'; }}Copy the code
Public class SheepTest {public static void main(String[] args) {Sheep Sheep = new Sheep(" Tom ", 1, "white "); Sheep sheepClone1 = new Sheep(sheep.getName(), sheep.getAge(), sheep.getColor()); Sheep sheepClone2 = new Sheep(sheep.getName(), sheep.getAge(), sheep.getColor()); System.out.println(sheep); System.out.println(sheepClone1); System.out.println(sheepClone2); }}Copy the code

General solution analysis

  1. The advantage is simple and easy to understand, easy to operate.
  2. When creating a new object, you always need to retrieve the properties of the original object, which is inefficient if the object is more complex.
  3. It is not flexible to always need to re-initialize an object rather than dynamically obtain its runtime state
  4. When an object extends a field, you need to modify the copied class, which violates the OCP rule.

In Java, the Object class is the root class of all classes. The Object class provides a clone() method that makes a copy of a Java Object. However, a Java class that implements Clone must implement an interface, Cloneable, which indicates that the class is replicable and has the ability to replicate = “prototype pattern”.

A basic introduction to the prototype pattern

  • Prototype is a pattern in which objects are created by specifying the types of objects that are created by using prototype instances, and new objects are created by copying those prototypes.
  • The prototype pattern is a creative design pattern that allows one object to create another customizable object without knowing the details of how to create it. When you create an object, you create a newObject by copying the current object, newObject= oldobject.clone ().

The Sheep class implements the Cloneable interface and overwrites the Clone method. The test class calls Clone ().

@Override
protected Object clone() throws CloneNotSupportedException {
    return super.clone();
}
Copy the code
Public static void main(String[] args) throws Exception {Sheep Sheep = new Sheep(" Tom ", 1, "white "," Sheep "); Sheep sheepClone1 = (Sheep) sheep.clone(); Sheep sheepClone2 = (Sheep) sheep.clone(); System.out.println(sheep); System.out.println(sheepClone1); System.out.println(sheepClone2); }Copy the code

Shallow copy and deep copy

Shallow copy

  • The data type is a basic data type or String, and the shallow copy directly passes the value, that is, the property value is copied directly to the new object.
  • The data type is a reference type (object, array), and the shallow copy is passed by reference, that is, the reference value (memory address) of the member variable is copied to the new object. If the property A is A reference type, then newObject.A= oldobject. A after the shallow copy. If A is changed, it will affect newObject and oldObject.
  • The above example of improved sheep cloning with prototypal mode is the shallow copy, which is implemented with the default clone(), super.Clone ().

Deep copy

Compared to the shallow copy, the deep copy handles the basic type or String type the same way, but handles the reference type. A deep copy applies for storage space for all member variables of reference types and copies the objects referenced by each reference data member variable until all objects reachable by the change object. Two ways to implement deep copy Method 1: Overwrite the Clone method to implement deep copy.

@ Override protected Object clone () throws CloneNotSupportedException {/ / for basic data types clone Sheep Sheep = (Sheep) super.clone(); Clone if (this.getBestGoodFriend()!); = null) { sheep.setBestGoodFriend((Sheep) this.bestGoodFriend.clone()); } return sheep; }Copy the code

Method 1 analysis: not easy to expand, if there are multiple reference types in sheep, it needs to manually clone(), and if there are reference types in the reference type, it needs to implement clone() for each reference type, operation is difficult to check.

Method 2: Implement deep copy through object serialization. The Sheep class implements Cloneable, Serializable

@Override protected Object clone() throws CloneNotSupportedException { ByteArrayOutputStream bos = null; ObjectOutputStream oos = null; ByteArrayInputStream bis = null; ObjectInputStream ois = null; Try {// serialize oos = new ObjectOutputStream(bos = new ByteArrayOutputStream()); oos.writeObject(this); Ois = new ObjectInputStream(bis = new ByteArrayInputStream(bos.tobytearray ())); return ois.readObject(); } catch (Exception e) {system.out.println (" serialization deserialization error "); } finally { try { bos.close(); oos.close(); bis.close(); ois.close(); } catch (Exception e) { } } return null; }Copy the code

Notes and details

  • When creating a new object is complex, you can use the prototype pattern to simplify the creation process while also improving efficiency.
  • Instead of reinitializing the object, the runtime state of the object is dynamically obtained.
  • If the original object changes (adding or subtracting attributes), the other clones will change accordingly, without modifying the code.
  • Cons: Deep cloning can require complex code. You need to have a Clone () per class, which is not too difficult for a brand new class, but when you modify an existing class, you need to modify its source code, which violates the OCP principle.