一、描述
举个栗子,快要过年了,对联是我们的传统文化也是过年的必须品。以前,都是自己买毛笔、墨水、红纸等等自己写。现在不一样了,由代工厂统一印制出来,品种多样,字体标准。大家只需要购买即可。(只是感觉缺少了点年味,呵呵~!)。
映射到java的世界中就相当于:自己写出来的可比作是new出来的对象,而由工厂印制出来的可类比于clone。今天讲的原型模式就是clone,它的好处显而易见,可以大量的印制且成本低廉(clone出来对象效率更高)。由此可见原型模式的主要作用就是复制对象。
二、实现
要想实现clone的类,必须具备两个条件:
- 实现Cloneable接口,虽然此接口没有需要实现的方法,但此接口的作用是在运行时通知虚拟机可以安全的在实现了此接口的类上使用clone方法,才可以被拷贝,否则就是抛出CloneNotSupportedException的异常。
- 重写Object的clone方法,我们都知道java中所有的对象都是Object的子类,在此父类中有一个clone方法,其作用就是返回一个对象的拷贝。但是它的作用域是Protected的,一般的类无法调用,所以需要重写成public的。
代码:
public class Couplet implements Cloneable,Serializable {
//一般属性
private String type;
//字数
private int count;
//数组属性
private String []chartArr;
//引用
private RedPaper redPaper;
public Couplet(String type) {
this.type = type;
}
//一般克隆
@Override
public Couplet clone()
throws CloneNotSupportedException {
return (Couplet)super.clone();
}
//深度克隆
public Couplet deepClone(){
try {
ByteArrayOutputStream bos = new ByteArrayOutputStream();
ObjectOutputStream out = new ObjectOutputStream(bos);
out.writeObject(this);
ObjectInputStream in = new ObjectInputStream(
new ByteArrayInputStream(bos.toByteArray()));
return (Couplet)in.readObject();
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
return null;
}
//getter and setter
}
public class RedPaper implements Serializable { //深度克隆时需要序列化
public RedPaper(){}
public RedPaper(int widthSize,int longSize){
this.widthSize = widthSize;
this.longSize = longSize;
}
private int widthSize;
private int longSize;
//getter and setter
}
测试:
public class PrototypeTest {
public static void main(String []args) {
try {
Couplet couplet = new Couplet("五字联");
couplet.setCount(5);
couplet.setChartArr(new String[]{"东户田民乐","南山席客多"});
couplet.setRedPaper(new RedPaper(30,200));
System.out.println("原对联对象:"+couplet);
System.out.println("原对联对象的ChartArr:"+couplet.getChartArr());
System.out.println("原对联对象的RedPaper:"+couplet.getRedPaper());
Couplet couplet1 = couplet.clone();
System.out.println("clone的对联对象:"+couplet1);
System.out.println("clone的对联对象的ChartArr:"+couplet1.getChartArr());
System.out.println("clone的对联对象的RedPaper:"+couplet1.getRedPaper());
Couplet couplet2 = couplet.deepClone();
System.out.println("deepClone的对联对象:"+couplet2);
System.out.println("deepClone的对联对象的ChartArr:"+couplet2.getChartArr());
System.out.println("deepClone的对联对象的RedPaper:"+couplet2.getRedPaper());
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
}
}
测试结果:
构造方法内执行了
原对联对象:com.xps.test.designPatterns.create.prototype.Couplet@7ea987ac
原对联对象的ChartArr:[Ljava.lang.String;@12a3a380
原对联对象的RedPaper:com.xps.test.designPatterns.create.prototype.RedPaper@29453f44
clone的对联对象:com.xps.test.designPatterns.create.prototype.Couplet@5cad8086
clone的对联对象的ChartArr:[Ljava.lang.String;@12a3a380
clone的对联对象的RedPaper:com.xps.test.designPatterns.create.prototype.RedPaper@29453f44
deepClone的对联对象:com.xps.test.designPatterns.create.prototype.Couplet@12edcd21
deepClone的对联对象的ChartArr:[Ljava.lang.String;@34c45dca
deepClone的对联对象的RedPaper:com.xps.test.designPatterns.create.prototype.RedPaper@52cc8049
从测试结果上可以看到:
- 构造方法只在new的时候执行了一次,而clone或深度clone时并没有执行构造函数
- clone出来的对象是另一个对象,但引用的数组和对象引用则并没有重新clone
- 深度克隆时包括引用的数据及对象都是新克隆出来的
总结
使用原型模式复制的对象并不会执行构造方法,直接无视构造函数,这与单例模式使用private限制只能new出来一个对象有冲突的地方。其次,复制对象是直接操作内存中的二进制流,特别是对很大的对象,性能相比new有很大的提升。所以,针对需要重复创建对象的应用,比如在循环中创建对象等可以考虑使用原型模式(但个性引用的数组和对象时需要特别注意)。