原型介绍
举个栗子:大话西游里,孙悟空对战牛魔王,孙悟空拔了一个猴毛一吹,变出了一堆“孙悟空”,这些悟空是孙悟空的副本,这就是原型模式,从一个原型中复制出N个副本。
实现
通过java 提供Object 类提供的clone(克隆)方法实现的。克隆把一个类的所有成员变量,包括变量和方法都被复制到一个新的实例中。原型对象需要实现 Cloneable
接口
原型模式
- 代码
public class WuKong implements Serializable, Cloneable{
private String name;
private int age;
public void skill(){
System.out.println("72变,火眼金睛");
}
get/set...
@Override
public String toString() {
return "WuKong{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
- 测试
public class MainTest {
public static void main(String[] args) throws CloneNotSupportedException {
WuKong wuKong = new WuKong();
wuKong.setName("齐天大圣");
wuKong.setAge(500);
wuKong.skill();
WuKong wuKong1 = (WuKong) wuKong.clone();
wuKong1.skill();
System.out.println(wuKong + " " + wuKong.hashCode());
System.out.println(wuKong1 + " " + wuKong1.hashCode());
System.out.println(wuKong == wuKong1);
}
}
克隆产生的新的对象
原型中拷贝
以上是原型的基本实现,但是在原型对象中都是基本数据类型,没有引用类型,Object 的克隆只能进行浅拷贝,也就是不会对引用类型之心拷贝,以上原型是不完整,继续改进
浅拷贝
原型类调整下,增加一个引用对象
- 原型类
public class WuKong implements Serializable, Cloneable{
private String name;
private int age;
private GlodenCudgel glodenCudgel;
public void skill(){
System.out.println("72变,火眼金睛");
}
get/set...
@Override
public String toString() {
return "WuKong{" +
"name='" + name + '\'' +
", age=" + age +
", glodenCudgel=" + glodenCudgel +
'}';
}
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
- 引用对象
public class GlodenCudgel implements Serializable, Cloneable{
private String g_name;
private long wget;
get/set...
@Override
public String toString() {
return "GlodenCudgel{" +
"g_name='" + g_name + '\'' +
", wget=" + wget +
'}';
}
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
- 测试浅拷贝
public class MainTest {
public static void main(String[] args) throws CloneNotSupportedException {
WuKong wuKong = new WuKong();
GlodenCudgel glodenCudgel = new GlodenCudgel();
glodenCudgel.setG_name("如意金箍棒");
glodenCudgel.setWget(1888);
wuKong.setName("齐天大圣");
wuKong.setAge(500);
wuKong.setGlodenCudgel(glodenCudgel);
WuKong wuKong1 = (WuKong) wuKong.clone();
wuKong.skill();
wuKong1.skill();
System.out.println("原型:"+wuKong + " " + wuKong.hashCode()+" 金箍棒:"+wuKong.getGlodenCudgel().hashCode());
System.out.println("克隆:"+wuKong1 + " " + wuKong1.hashCode()+" 金箍棒:"+wuKong1.getGlodenCudgel().hashCode());
System.out.println("原型==克隆:"+(wuKong == wuKong1));
}
}
原型方法与复制类引种对象hashcode 相同,是同一对象,修改原型中引用对象会修改克隆对象,显然不是我们所希望的,证明了 Clone 是浅拷贝,如何实现深拷贝,两种方式
深拷贝
使用clone 方式
在原型中重写clone 方法,主要是在克隆中对引应用类型进行重新赋值,其他不变
@Override
protected Object clone() throws CloneNotSupportedException {
WuKong wuKong = (WuKong) super.clone();
glodenCudgel = (GlodenCudgel)getGlodenCudgel().clone();
return wuKong;
}
重新执行测试方法,实现了深拷贝
通过克隆方式实现深拷贝,有弊端,如果有多个引用,需要每个引用类型都需要重新赋值,比较麻烦。另外原型模式需要实现 Cloneable 接口,违背了OCP 原则。
序列化方式
修改原形类,clone 方法不需要,添加一个深度拷贝方法, 其他方法属性保持不变
public Object deepCopy() {
ByteArrayOutputStream byteArrayOutputStream = null;
ObjectOutputStream objectOutputStream = null;
ByteArrayInputStream byteArrayInputStream = null;
ObjectInputStream objectInputStream = null;
try {
//序列化
byteArrayOutputStream = new ByteArrayOutputStream();
objectOutputStream = new ObjectOutputStream(byteArrayOutputStream);
objectOutputStream.writeObject(this);
//反序列化
byteArrayInputStream = new ByteArrayInputStream(byteArrayOutputStream.toByteArray());
objectInputStream = new ObjectInputStream(byteArrayInputStream);
Object o = objectInputStream.readObject();
return o;
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
byteArrayInputStream.close();
objectInputStream.close();
byteArrayOutputStream.close();
objectOutputStream.close();
} catch (Exception e1) {
e1.printStackTrace();
}
}
return null;
}
- 运行测试
总结
原型模式涉及到深拷贝和浅拷贝,尤其是使用到引用类型时,建议使用深度拷贝,深度拷贝使用序列化方式比较简单,推荐使用