意图

通过给出一个原型对象来指明所要创建对象的类型,然后通过复制这个原型对象来创建出更多同类型的对象。

类图与角色


java 使用Protocol Buffers java prototype_深复制


原型接口(Prototype):具体原型必须实现的接口,Java中的Cloneable接口。


具体原型(Concrete Prototype):对原型接口的实现。


Java对原型模式的内在支持


Java的所有类都继承自Object类,Object类提供了方法clone()进行对象复制,子类可以重写该方法提供满足自己需要的复制方法。


protected Object clone()


原型模式主要用于对象的复制,在java语言有一个Cloneable接口,它的作用只有一个,就是在运行时通知Java虚拟机可以安全地在这个类上使用clone方法,我们只需要调用这个clone()方法就可以得到一个对象的复制。

虽然Object类提供了clone()方法的实现,但是Object类本身并不实现Cloneable接口。并且,我们在“没有实现Cloneable接口的类”的对象上调用clone()方法将会抛出CloneNotSupportException异常。

实例

由于Object的clone方法作用域是protected类型的,一般的类无法调用,因此,Prototype类需要将clone方法的作用域修改为public。


class Prototype implements Cloneable{
    private String name;
    private int age;
	@Override
	public Prototype clone(){  
	        Prototype prototype = null;  
	        try{  
	            prototype = (Prototype)super.clone();//直接利用Object的clone方法进行对象复制  
	        }catch(CloneNotSupportedException e){  
	            e.printStackTrace();  
	        }  
	        return prototype;   
	}  
	public Prototype(String name,int age){
	        this.name=name;
	        this.age=age;
	}
	public void show(){  
	     	System.out.println("姓名:"+name+";年龄:"+age) ;
	}  
}
class test  {
	public static void main (String[] args) throws java.lang.Exception{
		Prototype prototype = new Prototype("void",25); 
		prototype.show();
		Prototype copy=prototype.clone();
		copy.show();
	}
}


深复制和浅复制


浅复制:Object类的clone方法,对于基本数据类型的属性拷贝其数据,对于对象类型(包括数组、容器)的属性只是拷贝其对象引用,而所有的对象引用都仍然指向原来的对象。
深复制。在浅复制基础上,将所有对象引用指向的对象复制一遍,并将对象引用指向复制后的新对象。


class Prototype implements Cloneable{
	private ArrayList<Integer> list = new ArrayList<Integer>();
	public Prototype(){
	    list.add(1);list.add(3);list.add(5);
	}
	@SuppressWarnings("unchecked")
	@Override
	public Prototype clone(){  
	        Prototype prototype = null;  
	        try{  
	            prototype = (Prototype)super.clone();  
	            prototype.list = (ArrayList<Integer>) this.list.clone();//这里,显示的将对象类型的属性进行复制
	        }catch(CloneNotSupportedException e){  
	            e.printStackTrace();  
	        }  
	        return prototype;   
	}  
	public ArrayList<Integer> getList(){
	    return list;
	}
	public void show(){  
	     	for(Integer i:list){
	     	    System.out.print(i+" ");
	     	} 
	}  
}
class test  {
	public static void main (String[] args) throws java.lang.Exception{
		Prototype prototype = new Prototype(); 
		prototype.show();
		System.out.println();
		Prototype copy=prototype.clone();
		copy.show();
		System.out.println();
		System.out.println(prototype.getList()==copy.getList());//深复制后,引用不再是指向同一个对象
	}
}

利用序列化来做深复制



把对象写到流里的过程是序列化过程,将对象从流中读出来称为反序列化过程。在Java语言里深复制一个对象,常常可以先使对象实现Serializable接口,然后把对象写到一个流里,再从流里读回来,便可以重建对象。



这样做的前提是对象以及对象内部所有引用到的对象都是可序列化的 ,否则,需要将那些不可序列化的对象设置为transient,排除在复制过程之外。



适用场景



当要实例化的类是在运行时刻指定时,例如,通过动态装载;
建立相应数目的原型并克隆它们可能比每次用合适的状态手工实例化该类更方便的时候。



优点



使用原型模式创建对象比直接new一个对象在性能上要好的多,因为Object类的clone方法是一个本地方法,它直接操作内存中的二进制流,特别是复制大对象时,性能的差别非常明显。
使用原型模式的另一个好处是简化对象的创建,使得创建对象就像我们在编辑文档时的复制粘贴一样简单。