标准的原型模式代码应该这样设计,先创建原型Prototype接口:
package nju.java.pattern.prototype_pattern;
public interface Prototype {
Prototype clone();
}
具体需要克隆的类ConcretePrototypeA:
package nju.java.pattern.prototype_pattern;
import java.util.List;
public class ConcretePrototypeA implements Prototype{
private int age;
private String name;
private List hobbbies;
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public List getHobbbies() {
return hobbbies;
}
public void setHobbbies(List hobbbies) {
this.hobbbies = hobbbies;
}
@Override
public ConcretePrototypeA clone() {
ConcretePrototypeA concretePrototypeA=new ConcretePrototypeA();
concretePrototypeA.setAge(this.age);
concretePrototypeA.setName(this.name);
concretePrototypeA.setHobbbies(this.hobbbies);
return concretePrototypeA;
}
}
Client类:
package nju.java.pattern.prototype_pattern;
public class Client {
private Prototype prototype;
public Client(Prototype prototype){
this.prototype=prototype;
}
public Prototype startClone(Prototype concretePrototype){
return (Prototype)concretePrototype.clone();
}
}
测试代码如下:
package nju.java.pattern.prototype_pattern;
import java.util.ArrayList;
public class PrototypeTest {
public static void main(String[] args) {
//创建一个具体的需要克隆的对象
ConcretePrototypeA concretePrototype=new ConcretePrototypeA();
//随便填写点属性
concretePrototype.setAge(22);
concretePrototype.setName("原型");
concretePrototype.setHobbbies(new ArrayList());
System.out.println(concretePrototype);
//创建Client对象 开始克隆
Client client=new Client(concretePrototype);
ConcretePrototypeA concretePrototypeClone=(ConcretePrototypeA)client.startClone(concretePrototype);
System.out.println(concretePrototypeClone);
//测试其他的
System.out.println("克隆对象中的引用类型地址值:"+concretePrototypeClone.getHobbbies());
System.out.println("原对象中的引用类型地地址值:"+concretePrototype.getHobbbies());
System.out.println("对象地址比较:"+(concretePrototype.getHobbbies()==concretePrototypeClone.getHobbbies()));
}
}
运行结果如下:
可以看到hobbies的引用地址相同,意味着复制的不是值,而是引用的地址。如果我们修改任意一个对象的属性值,则concretePrototype和concretePrototypeClone的hobbies都会改变,这就是我们常说的潜克隆。潜克隆只是完整复制了值类型数据,没有赋值引用对象,换句话说,所有的引用对象仍然指向原来的对象,显然这不是我们想要的结果。
书中对于深克隆的介绍换了另一个场景,一个我们中国人熟知的故事。齐天大圣与他的金箍棒,齐天大圣可以七十二变,也可以拔一根毫毛吹出千万猴子猴孙,他的武器金箍棒可大可小,伸缩自如。这就是我们耳熟能详的原型模式经典体现。
创建原型猴子类Monkey:
package nju.java.pattern.prototype_pattern;
import java.util.Date;
public class Monkey {
public int height;
public int weight;
public Date birthday;
}
创建引用对象金箍棒类Jingubang:
package nju.java.pattern.prototype_pattern;
import java.io.Serializable;
public class Jingubang implements Serializable {
public float h=100;
public float d=10;
public void big(){
this.d*=2;
this.h*=2;
}
public void small(){
this.d/=2;
this.h/=2;
}
}
具体对象类齐天大圣QiTianDaSheng:
package nju.java.pattern.prototype_pattern;
import java.io.*;
import java.util.Date;
public class QiTianDaSheng extends Monkey implements Cloneable, Serializable {
public Jingubang jingubang;
public QiTianDaSheng(){
this.birthday=new Date();
this.jingubang=new Jingubang();
}
@Override
protected Object clone() throws CloneNotSupportedException {
return this.deepClone();
}
private Object deepClone() {
try{
ByteArrayOutputStream bos=new ByteArrayOutputStream();
ObjectOutputStream oos=new ObjectOutputStream(bos);
oos.writeObject(this);
ByteArrayInputStream bis=new ByteArrayInputStream(bos.toByteArray());
ObjectInputStream ois=new ObjectInputStream(bis);
QiTianDaSheng copy=(QiTianDaSheng)ois.readObject();
copy.birthday=new Date();
return copy;
}catch (Exception e){
e.printStackTrace();
return null;
}
}
public QiTianDaSheng shallowClone(QiTianDaSheng target){
QiTianDaSheng qiTianDaSheng=new QiTianDaSheng();
qiTianDaSheng.height=target.height;
qiTianDaSheng.weight=target.weight;
qiTianDaSheng.jingubang=target.jingubang;
qiTianDaSheng.birthday=new Date();
return qiTianDaSheng;
}
}
测试代码:
package nju.java.pattern.prototype_pattern;
public class DeepCloneTest {
public static void main(String[] args) {
QiTianDaSheng qiTianDaSheng=new QiTianDaSheng();
try{
QiTianDaSheng clone=(QiTianDaSheng)qiTianDaSheng.clone();
System.out.println("深克隆:"+(qiTianDaSheng.jingubang==clone.jingubang));
}catch (Exception e){
e.printStackTrace();
}
QiTianDaSheng q=new QiTianDaSheng();
QiTianDaSheng n=q.shallowClone(q);
System.out.println("潜克隆:"+(q.jingubang==n.jingubang));
}
}
测试结果如下:
如果我们克隆的目标对象是单例对象,那么意味着深克隆会破坏单例模式。实际上防止克隆破坏单例模式的解决思路非常简单,禁止深克隆即可。要么我们的单例类不实现Cloneable接口,要么我们重写克隆clone()方法,在clone()方法中返回单例对象即可。
@Override
protected Object clone() throws CloneNotSupportedException{
return INSTANCE;
}
我们常用的ArrayList实现了Cloneable接口,来看clone方法的源码:
public Object clone() {
try {
ArrayList> v = (ArrayList>) super.clone();
v.elementData = Arrays.copyOf(elementData, size);
v.modCount = 0;
return v;
} catch (CloneNotSupportedException e) {
// this shouldn't happen, since we are Cloneable
throw new InternalError(e);
}
}
代码位置:https://github.com/YellowDii/MySpring.git
参考书籍:《Spring5核心原理与30个类手写实现》