用原型實例指定建立對象的種類,而且經過拷貝這些原型建立新的對象java
- Client:客戶端用戶。
- Prototype:抽象類或者接口,聲明具有Clone能力。
- ConcretePrototype:具體的原型類。
- 性能優良:
- 原型模式是在內存二進制流的拷貝,要比直接new一個對象性能好不少,特別是要在一個循環體內產生大量的對象時,原型模式能夠更好地體現其優勢。
- 逃避構造函數的約束:
- 這既是它的優勢也是缺點,直接在內存中拷貝,構造函數是不會執行的。所以,若是在構造函數中須要一些特殊的初始化操做的類型,在使用Cloneable實現拷貝時,須要注意構造函數不會執行的問題。
一、類初始化須要消化很是多的資源,這個資源包括數據、硬件資源等,經過原型拷貝避免這些消耗。數組
二、經過new產生一個對象須要很是繁瑣的數據準備或訪問權限,這時可使用原型模式。ide
三、一個對象須要提供給其餘對象訪問,並且各個調用者可能都須要修改其值,同時有可能每一個對象中有一些共有屬性時,能夠考慮使用原型模式拷貝多個對象提供調用者使用,即保護性拷貝。函數
PS:須要注意的是,經過實現Cloneable接口的原型模式在調用clone函數構造實例時並不必定比經過new操做速度快,只有當經過new構造對象較爲耗時或者說成本較高時,經過clone方法才能得到效率上的提高。所以,在使用Cloneable時須要考慮構建對象的成本以及作一些效率上的測試。固然,實現原型模式也不必定非要實現Cloneable,也有其餘的實現方式。性能
// 簡單的可拷貝對象 public class Thing implements Cloneable { // 構造函數,在拷貝對象時不會被調用 public Thing() { System.out.println("構造函數被執行了..."); } @Override protected Object clone() throws CloneNotSupportedException { Thing thing = null; try { thing = (Thing) super.clone(); } catch (CloneNotSupportedException e) { e.printStackTrace(); } return thing; } } // 簡單的場景類 public class Client { public static void main(String[] args) { // 產生一個對象 Thing thing = new Thing(); // 拷貝一個對象 Thing cloneThing = thing.clone(); } }運行結果:測試
構造函數被執行了...
Object類提供的方法clone只是拷貝本對象(和其中的全局原始類型,好比int、long、char等,還有String),其對象內部的數組、引用對象等都不拷貝,仍是指向原生對象的內部元素地址。this
淺拷貝的例子:spa
public class Thing implements Cloneable { // 定義一個私有變量 private ArrayList<String> arrayList = new ArrayList<>(); @Override protected Object clone() throws CloneNotSupportedException { Thing thing = null; try { thing = (Thing) super.clone(); } catch (CloneNotSupportedException e) { e.printStackTrace(); } return thing; } //設置arrayList的值 public void setValue(String value) { this.arrayList.add(value); } // 取得arrayList的值 public ArrayList<String> getValue() { return this.arrayList; } } public class Client { public static void main(String[] args) { // 產生一個對象 Thing thing = new Thing(); // 設置一個值 thing.setValue("張三"); // 拷貝一個對象 Thing cloneThing = thing.clone(); cloneThing.setValue("李四"); System.out.println(thing.getValue()); } }運行結果:code
[張三, 李四]由於經過clone拷貝出來的cloneThing對象中的arrayList沒有被單獨拷貝,和thing對象共用一份arrayList,因此改變了cloneThing對象中的arrayList也就改變了thing對象中的arrayList。對象
進行深度的徹底拷貝。兩個對象之間沒有任何的瓜葛了。
深拷貝的例子:
public class Thing implements Cloneable { // 定義一個私有變量 private ArrayList<String> arrayList = new ArrayList<>(); @Override protected Object clone() throws CloneNotSupportedException { Thing thing = null; try { thing = (Thing) super.clone(); // 對arrayList也進行拷貝 thing.arrayList = (ArrayList<String>) this.arrayList.clone(); } catch (CloneNotSupportedException e) { e.printStackTrace(); } return thing; } //設置arrayList的值 public void setValue(String value) { this.arrayList.add(value); } // 取得arrayList的值 public ArrayList<String> getValue() { return this.arrayList; } } public class Client { public static void main(String[] args) { // 產生一個對象 Thing thing = new Thing(); // 設置一個值 thing.setValue("張三"); // 拷貝一個對象 Thing cloneThing = thing.clone(); cloneThing.setValue("李四"); System.out.println(thing.getValue()); } }運行結果:
[張三]由於對arrayList也進行了獨立的拷貝,因此修改cloneThing對象中的arrayList並不會影響thing對象中的arrayList值。
對象的clone與對象內的final關鍵字是有衝突的。好比上面的例子中,若是arrayList有final屬性,在clone處,編譯器會報錯。
因此,要使用clone方法,類的成員變量上不要增長final關鍵字。
Intent的clone方法,使用的拷貝構造函數的方式來實現。其內部並無調用super.clone(),而是調用了new Intent(this)。正如上文提到的,使用clone和new須要根據構造對象的成原本決定,若是對象的構形成本比較高或者構造較爲麻煩,那麼使用clone()函數效率較高,不然可使用new的形式。
@Override public Object clone() { return new Intent(this); } /** * Copy constructor. */ public Intent(Intent o) { this.mAction = o.mAction; this.mData = o.mData; this.mType = o.mType; this.mPackage = o.mPackage; this.mComponent = o.mComponent; this.mFlags = o.mFlags; this.mContentUserHint = o.mContentUserHint; if (o.mCategories != null) { this.mCategories = new ArraySet<String>(o.mCategories); } if (o.mExtras != null) { this.mExtras = new Bundle(o.mExtras); } if (o.mSourceBounds != null) { this.mSourceBounds = new Rect(o.mSourceBounds); } if (o.mSelector != null) { this.mSelector = new Intent(o.mSelector); } if (o.mClipData != null) { this.mClipData = new ClipData(o.mClipData); } }