原型模式就是通过复制现在已经存在的对象来创建一个新的对象。
原型模式一般包含两种角色
1.抽象原型类
2.具体原型类
java本身的object已经实现了原型模式,它提供了一个clone()方法,作用是返回对象的一个拷贝,但是其作用域protected类型的,一般的类无法调用,因此,我们需要将clone方法的作用域修改为public类型。
另外java还提供了一个Cloneable接口,它的作用只有一个,就是在运行时通知虚拟机可以安全地在实现了此接口的类上使用clone方法。在java虚拟机中,只有实现了这个接口的类才可以被拷贝,否则在运行时会抛出CloneNotSupportedException异常。
实现如下
此处抽象原型类为object类
具体原型类为ConcretePrototype类
public class ConcretePrototype implements Cloneable{
private ConcretePrototypeMethod m1;
private String m2;
private int m3;
public Object clone(){
try {
return super.clone();
} catch (CloneNotSupportedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return null;
}
public ConcretePrototypeMethod getM1() {
return m1;
}
public void setM1(ConcretePrototypeMethod m1) {
this.m1 = m1;
}
public String getM2() {
return m2;
}
public void setM2(String m2) {
this.m2 = m2;
}
public int getM3() {
return m3;
}
public void setM3(int m3) {
this.m3 = m3;
}
}
//为具体原型类提供一个对象属性
public class ConcretePrototypeMethod {
private String m;
public String getM() {
return this.m;
}
public void setM(String m) {
this.m = m;
}
}
//测试代码
public class Test {
/**
* @param args
*/
public static void main(String[] args) {
ConcretePrototype prototype1 = new ConcretePrototype();
ConcretePrototypeMethod m1 = new ConcretePrototypeMethod();
m1.setM("this is object1");
prototype1.setM1(m1);
prototype1.setM2("this is string1");
prototype1.setM3(1);
ConcretePrototype prototype2 = (ConcretePrototype) prototype1.clone();
System.out.println(prototype2.getM1().getM());
System.out.println(prototype2.getM2());
System.out.println(prototype2.getM3());
prototype1.getM1().setM("object1 is change");
prototype1.setM2("string1 is change");
prototype1.setM3(2);
System.out.println(prototype2.getM1().getM());
System.out.println(prototype2.getM2());
System.out.println(prototype2.getM3());
}
}
输出如下
this is object1
this is string1
1
object1 is change
this is string1
1
我们发现对于克隆出来的对象prototype2,如果改变源对象prototype1的属性m1,也会导致prototype2对象的属性发生变化。这是因为深拷贝和浅拷贝的原因。java的object的colne方法中只会对八种基本类型和String类型进行深拷贝,而不会对其他的类型进行深拷贝,也就是说,其他的如引用类型clone方法拷贝过去的也只是该对象的引用。这就导致了拷贝出来的对象和源对象都持有同一个引用,那么,只要由一个对象对这个引用代表的对象进行了操作,另一个对象也会受到其影响。那么我们有什么方法可以让引用对象也进行深拷贝呢,这里介绍一种方法。
将需要复制的对象放入流中,再将其取出
import java.io.Serializable;
public class ConcretePrototype implements Serializable{
private ConcretePrototypeMethod m1;
private String m2;
private int m3;
public ConcretePrototypeMethod getM1() {
return m1;
}
public void setM1(ConcretePrototypeMethod m1) {
this.m1 = m1;
}
public String getM2() {
return m2;
}
public void setM2(String m2) {
this.m2 = m2;
}
public int getM3() {
return m3;
}
public void setM3(int m3) {
this.m3 = m3;
}
}
import java.io.Serializable;
public class ConcretePrototypeMethod implements Serializable{
private String m;
public String getM() {
return this.m;
}
public void setM(String m) {
this.m = m;
}
}
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.OutputStream;
public class Test {
/**
* @param args
*/
public static void main(String[] args) {
ConcretePrototype prototype1 = new ConcretePrototype();
ConcretePrototypeMethod m1 = new ConcretePrototypeMethod();
m1.setM("this is object1");
prototype1.setM1(m1);
prototype1.setM2("this is string1");
prototype1.setM3(1);
ConcretePrototype prototype2 = null;
try
{
if (prototype1 != null)
{
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(baos);
oos.writeObject(prototype1);
oos.close();
ByteArrayInputStream bais = new ByteArrayInputStream(baos
.toByteArray());
ObjectInputStream ois = new ObjectInputStream(bais);
prototype2 = (ConcretePrototype) ois.readObject();
ois.close();
}
} catch (IOException e)
{
e.printStackTrace();
} catch (ClassNotFoundException e)
{
e.printStackTrace();
}
System.out.println(prototype2.getM1().getM());
System.out.println(prototype2.getM2());
System.out.println(prototype2.getM3());
prototype1.getM1().setM("object1 is change");
prototype1.setM2("string1 is change");
prototype1.setM3(2);
System.out.println(prototype2.getM1().getM());
System.out.println(prototype2.getM2());
System.out.println(prototype2.getM3());
}
}
输出结果
this is object1
this is string1
1
this is object1
this is string1
1
可以看出,两个对象所持有的引用都不一样,达到了深克隆的效果