意圖
通過給出一個原型對象來指明所要建立對象的類型,然後通過複制這個原型對象來建立出更多同類型的對象。
類圖與角色
原型接口(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方法是一個本地方法,它直接操作記憶體中的二進制流,特别是複制大對象時,性能的差别非常明顯。
使用原型模式的另一個好處是簡化對象的建立,使得建立對象就像我們在編輯文檔時的複制粘貼一樣簡單。