In some systems, there may be a large number of identical or similar objects, and the creation of such objects can be complex and costly. Using the prototype pattern described in this section can solve such problems.
The prototype pattern
The Prototype pattern is defined as follows: Use an already created object to create identical or similar objects by copying the Prototype object. You don’t need to know the details of creating an object.
Prototype pattern structure and implementation
Java provides a Clone () method for objects, so implementing the prototype pattern in Java is simple.
- Pattern structure
The archetypal pattern contains the following primary roles.
- Abstract stereotype class: specifies the interface that concrete stereotype objects must implement, in this case
Cloneable
Interface. - Concrete stereotype class: The Clone () method that implements the abstract stereotype class, which is an object that can be copied.
- Access classes: Copy new objects using the clone() method in the concrete prototype class.
If, when the clone doesn’t implement Cloneable interface, the compiler will throw a CloneNotSupportedException anomalies.
Prototype structure drawing:
In general, using the prototype pattern (Java Clone ()) requires the following conditions:
- For any object o, there is O.lone ()! = o. In other words, the clone object is not the same object as the prototype object.
- For any object o, there is o.lone ().getClass() == o.getclass (). In other words, the clone is of the same type as the prototype.
- If the equals() method of object O is properly defined, o.lone ().equals(o) should be established.
Code example: Here by writing a Monkey class, and then through the clone method, copy the monkey object.
- Realizetype (replicable object)
public class Wukong implements Cloneable{
@Override
protected Object clone(a) throws CloneNotSupportedException {
return super.clone();
}
private String name;
private int age;
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;
}
@Override
public String toString(a) {
return "[name=" + name + ", age=" + age + "]"; }}Copy the code
- Use the client
public class Client {
public static void main(String[] args) throws CloneNotSupportedException {
Wukong wukong = new Wukong();
wukong.setName("Monkey King");
wukong.setAge(500);
System.out.println("wukong:"+wukong);
Wukong wukong2 = (Wukong)wukong.clone();
System.out.println("wukong2:"+wukong2); System.out.println(wukong == wukong2); }}Copy the code
The execution result
Wukong :[name= qi Tian Da Sheng, age=500] wukong2:[name= age=500]
false
Copy the code
The copied objects have the same content, but not the same address. Two objects with exactly the same content, which is exactly what our prototype copy looks like.
Deep copy & shallow copy
Here’s another code example. The object properties we copied above are simple base types.
- Prototype Object 1
/** * Deep copy target object *@author fengwei
*
*/
public class DeepCloneOjb implements Serializable.Cloneable {
private static final long serialVersionUID = 1L;
private String name;
private Integer age;
public String getName(a) {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
protected Object clone(a) throws CloneNotSupportedException {
return super.clone();
}
@Override
public String toString(a) {
return ", DeepCloneOjb hashcode = [" + hashCode() + " ] , data = [name=" + name + ", age=" + age + "]";
}
public Integer getAge(a) {
return age;
}
public void setAge(Integer age) {
this.age = age; }}Copy the code
- Prototype Object 2
public class DeepPrototype implements Cloneable.Serializable{
private static final long serialVersionUID = 1L;
private String userName;
private DeepCloneOjb deepCloneOjb;
public String getUserName(a) {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
public DeepCloneOjb getDeepCloneOjb(a) {
return deepCloneOjb;
}
public void setDeepCloneOjb(DeepCloneOjb deepCloneOjb) {
this.deepCloneOjb = deepCloneOjb;
}
@Override
protected Object clone(a) throws CloneNotSupportedException {
return super.clone();
}
@Override
public String toString(a) {
return "DeepPrototype hashcode = "+ hashCode() +", data = [userName=" + userName + ", deepCloneOjb=" + deepCloneOjb + "]"; }}Copy the code
- Use the client
public static void main(String[] args) throws CloneNotSupportedException {
DeepCloneOjb deepCloneOjb = new DeepCloneOjb();
deepCloneOjb.setName("deepCloneOjb");
deepCloneOjb.setAge(10);
// Prototype objects
DeepPrototype deepPrototype = new DeepPrototype();
deepPrototype.setUserName("deepPrototype");
deepPrototype.setDeepCloneOjb(deepCloneOjb);
//clone
DeepPrototype deepPrototype2 = (DeepPrototype) deepPrototype.clone();
System.out.println("deepPrototype="+deepPrototype);
System.out.println("deepPrototype2="+deepPrototype2);
}
Copy the code
Execution Result:
deepPrototype=DeepPrototype hashcode = 2018699554, data = [userName=deepPrototype, deepCloneOjb=, DeepCloneOjb hashcode = [1311053135 ] , data = [name=deepCloneOjb, age=10]]
deepPrototype2=DeepPrototype hashcode = 118352462, data = [userName=deepPrototype, deepCloneOjb=, DeepCloneOjb hashcode = [1311053135 ] , data = [name=deepCloneOjb, age=10]]
Copy the code
The result is that the address of the DeepPrototype object is different from that of deepPrototype2, but the address of the internal DeepCloneOjb object points to the same object.
This is the deep-copy problem. Java provides Clone, which only works with simple basic data types, and copies object addresses for reference data types (classes, interfaces, arrays, and other complex types).
Deep copy solution
We can implement deep copy by nesting copies, layer by layer. Clone () = clone(); clone() = clone();
@Override
protected Object clone(a) throws CloneNotSupportedException {
// The basic data types (attributes) and strings are cloned
Object deep = super.clone();
// Handle attributes of the reference type separately
DeepPrototype deepProtoType = (DeepPrototype)deep;
deepProtoType.deepCloneOjb = (DeepCloneOjb)deepCloneOjb.clone();
return deepProtoType;
}
Copy the code
Execution Result:
deepPrototype=DeepPrototype hashcode = 2018699554, data = [userName=deepPrototype, deepCloneOjb=, DeepCloneOjb hashcode = [1311053135 ] , data = [name=deepCloneOjb, age=10]]
deepPrototype2=DeepPrototype hashcode = 118352462, data = [userName=deepPrototype, deepCloneOjb=, DeepCloneOjb hashcode = [1550089733 ] , data = [name=deepCloneOjb, age=10]]
Copy the code
Discover the internal reference type object address is also different!
Effects of cloning on singletons
We know that cloning means making a copy of a target object that is identical in content. For a certain system, if the target object happens to be a singleton object, can we destroy the singleton characteristics by cloning?
Code examples:
public class ReflectClone implements Cloneable {
private static ReflectClone instance = new ReflectClone();
private ReflectClone(a) {}public static ReflectClone getInstance(a) {
return instance;
}
@Override
public ReflectClone clone(a) {
try {
return (ReflectClone) super.clone();
} catch (CloneNotSupportedException e) {
return null; }}public static void main(String[] args) {
// Prototype objects
ReflectClone protoType = ReflectClone.getInstance();
//cloneReflectClone cloneType = protoType.clone(); System.out.println(protoType==cloneType); }}Copy the code
Execution result False
As a result, two different objects are indeed created. In fact, the solution to preventing copy destruction of singletons is very simple, just forbid copy. So our singleton class does not implement the Cloneable interface, so we can either rewrite the clone() method and return the singleton in the clone() method, as follows.
@Override
public ConcretePrototype clone(a) {
return instance;
}
Copy the code
Application of prototype patterns in source code
ArrayList implements the Cloneable interface and rewrites the Clone method.
public Object clone(a) {
try {
@SuppressWarnings("unchecked")
ArrayList<E> v = (ArrayList<E>) super.clone();
v.elementData = Arrays.copyOf(elementData, size);
v.modCount = 0;
return v;
} catch (CloneNotSupportedException e) {
throw newInternalError(); }}Copy the code
As you can see from the above code, the clone() method simply loops through the elements in the List. If the object in collection A is a reference type, would the clone object in ArrayList now be a deep copy or a shallow copy?
Add a new class that uses ArrayList to reference variables and clone the contents of the collection. The code is as follows:
public class DeepPrototypeList implements Cloneable.Serializable{
private static final long serialVersionUID = 1L;
private String userName;
private ArrayList<DeepCloneOjb> deepCloneOjbList;
public String getUserName(a) {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
public ArrayList<DeepCloneOjb> getDeepCloneOjb(a) {
return deepCloneOjbList;
}
public void setDeepCloneOjb(ArrayList<DeepCloneOjb> deepCloneOjb) {
this.deepCloneOjbList = deepCloneOjb;
}
@Override
protected Object clone(a) throws CloneNotSupportedException {
// The basic data types (attributes) and strings are cloned
Object deep = super.clone();
// Handle attributes of the reference type separately
DeepPrototypeList deepProtoType = (DeepPrototypeList)deep;
deepProtoType.deepCloneOjbList = (ArrayList<DeepCloneOjb>) ((ArrayList)deepProtoType.deepCloneOjbList).clone();
return deepProtoType;
}
public Object deepClone(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); // The current object is output as a stream of objects
// deserialize
bis = new ByteArrayInputStream(bos.toByteArray());
ois = new ObjectInputStream(bis);
DeepPrototypeList copyObj = (DeepPrototypeList)ois.readObject();
return copyObj;
} catch (Exception e) {
e.printStackTrace();
return null;
} finally {
/ / close the flow
try {
bos.close();
oos.close();
bis.close();
ois.close();
} catch(Exception e2) { System.out.println(e2.getMessage()); }}}@Override
public String toString(a) {
return "DeepPrototypeList hashcode = "+hashCode()+", [userName=" + userName + ", deepCloneOjbList=" + deepCloneOjbList + "]"; }}Copy the code
- Using the
public static void main(String[] args) throws CloneNotSupportedException {
DeepCloneOjb deepCloneOjb = new DeepCloneOjb();
deepCloneOjb.setName("deepCloneOjb");
deepCloneOjb.setAge(10);
ArrayList<DeepCloneOjb> list = new ArrayList<>();
list.add(deepCloneOjb);
DeepPrototypeList deepPrototypeList = new DeepPrototypeList();
deepPrototypeList.setUserName("deepPrototypeList");
deepPrototypeList.setDeepCloneOjb(list);
System.out.println("DeepPrototypeList =="+deepPrototypeList.toString());
DeepPrototypeList deepPrototypeList2 = (DeepPrototypeList)deepPrototypeList.deepClone();
System.out.println("Serialization mode deepPrototypeList2=="+deepPrototypeList2.toString());
DeepPrototypeList deepPrototypeList3 = (DeepPrototypeList)deepPrototypeList.clone();
System.out.println("Set provides cloning methods deepPrototypeList3=="+deepPrototypeList3.toString());
}
Copy the code
Execution Result:
Source object deepPrototypeList== deepPrototypeList hashCode =2018699554, [userName=deepPrototypeList, deepCloneOjbList=[, DeepCloneOjb hashcode = [1311053135 ] , data = [name=deepCloneOjb, age=10DeepPrototypeList2 ==DeepPrototypeList hashCode =2065951873, [userName=deepPrototypeList, deepCloneOjbList=[, DeepCloneOjb hashcode = [1791741888 ] , data = [name=deepCloneOjb, age=10DeepPrototypeList3 ==DeepPrototypeList hashCode =1595428806, [userName=deepPrototypeList, deepCloneOjbList=[, DeepCloneOjb hashcode = [1311053135 ] , data = [name=deepCloneOjb, age=10]]]
Copy the code
As can be seen from the clone method in ArrayList, the object addresses in the collection are still the same. Therefore, it can be concluded that the clone method in ArrayList is a shallow copy.
Is there a way to do deep copy, as the code shows, we can do deep copy by serialization.