Java面試通關手冊(Java學習指南,歡迎Star,會一直完善下去,歡迎建議和指導):https://github.com/Snailclimb/Java_Guidehtml
一隻準程序猿的嘮叨github
[TOC]安全
在面向對象系統中,使用原型模式來複制一個對象自身,從而克隆出多個與原型對象如出一轍的對象。服務器
另外在軟件系統中,有些對象的建立過程較爲複雜,並且有時候須要頻繁建立,原型模式經過給出一個原型對象來指明所要建立的對象的類型,而後用複製這個原型對象的辦法建立出更多同類型的對象,這就是原型模式的意圖所在。微信
GOF給出的原型模式定義以下:
Specify the kind of objects to create using a prototypical instance, and create new objects by copying this prototype. (使用原型實例指定將要建立的對象類型,經過複製這個實例建立新的對象。)
咱們如今通常會使用new關鍵字指定類名生成類的實例(PS:咱們之前使用java.lang.Cloneable
的一個很大緣由是使用new建立對象的速度相對來講會慢一些,隨着JVM性能的提高,new的速度和Object的clone()方法
的速度差很少了。)。
使用new關鍵字建立類的時候必須指定類名,可是在開發過程當中也會有「在不指定類名的前提下生成實例」的需求。例如,在下面這些狀況下,就須要根據現有的實例來生成新的實例。
1) 對象種類繁多,沒法將他們整合到一個類的時候;
2) 難以根據類生成實例時;
3) 想解耦框架與生成的實例時。
若是想要讓生成實例的框架再也不依賴於具體的類,這時,不能指定類名來生成實例,而要事先「註冊」一個「原型」實例,而後經過複製該實例來生成新的實例。
在原型模式結構中定義了一個抽象原型類,全部的Java類都繼承自 java.lang.Object
,而Object類提供一個clone()方法
,能夠將一個Java對象複製一份。所以在Java中能夠直接使用Object提供的clone()方法來實現對象的克隆,Java語言中的原型模式實現很簡單。
可以實現克隆的Java類必須實現一個標識接口Cloneable
,表示這個Java類支持複製。若是一個類沒有實現這個接口可是調用了clone()方法
,Java編譯器將拋出一個CloneNotSupportedException
異常。
注意: `java.lang.Cloneable
只是起到告訴程序能夠調用clone方法的做用,它自己並無定義任何方法。
在使用原型模式克隆對象時,根據其成員對象是否也克隆,原型模式能夠分爲兩種形式:深克隆 和 淺克隆 。
關於深克隆 和 淺克隆 的詳細內容能夠參考:詳解Java中的clone方法
原型模式的優勢:
原型模式的缺點:
下面示例程序的做用是將字符串放入方框中顯示出來或者是加了下劃線顯示出來。
類和接口一覽表:
示例程序類圖:
Product接口
是複製功能接口,該接口繼承了java.lang.Cloneable
(只有實現了該接口的類的實例才能夠調用clone()方法
複製實例,不然會拋出異常).
另外須要注意:`java.lang.Cloneable
只是起到告訴程序能夠調用clone方法的做用,它自己並無定義任何方法。
package prototype_pattern; public interface Product extends Cloneable{ //use方法是用於「使用」的方法,具體怎麼「使用」,則被交給子類去實現。 public abstract void use(String s); //creatClone方法是用於複製實例的方法 public abstract Product creatClone(); }
Manager類使用Product接口來複制實例。
Product接口
以及Manager類
的代碼徹底沒有出如今MessageBox類
和UnderlinePen類
的名字,所以這意味着咱們能夠獨立地修改Product接口
以及Manager類
,不受MessageBox類
和UnderlinePen類
的影響。這是很是重要的,由於 一旦在類中使用到了別的類名,就意味着該類與其餘類緊密的地耦合在了一塊兒 。在Manager類
中,並無寫明具體的類名, 僅僅使用了Product
這個接口名。也就是說,Product接口
成爲了鏈接Manager類
與其餘具體類之間的橋樑。
package prototype_pattern; import java.util.HashMap; public class Manager { //保存實例的「名字」和「實例」之間的對應關係 private HashMap<String, Product> showcase=new HashMap<String, Product>(); //register方法將接收到的一組「名字」和「Product接口」註冊到showcase中。這裏Product是實現Product接口的實例,具體還未肯定 public void register(String name ,Product product){ showcase.put(name, product); } public Product create(String productname){ Product p=showcase.get(productname); return p.creatClone(); } }
裝飾方框樣式的具體原型,實現了 Product接口
,實現複製現有實例並生成新實例的方法。
package prototype_pattern; public class MessageBox implements Product { //保存的是裝飾方框使用的字符樣式 private char decochar; public MessageBox(char decochar) { this.decochar = decochar; } @Override public void use(String s) { int length=s.getBytes().length; for (int i = 0; i < length+4; i++) { System.out.print(decochar); } System.out.println(""); System.out.println(decochar+" "+s+" "+decochar); for (int i = 0; i < length+4; i++) { System.out.print(decochar); } System.out.println(""); } //該方法用於複製本身 @Override public Product creatClone() { Product p=null; try { p=(Product) clone(); } catch (CloneNotSupportedException e) { e.printStackTrace(); } return p; } }
只有類本身(或是它的子類)可以調用Java語言中定義的clone方法。當其餘類要求複製實例時,必須先調用createClone這樣的方法,而後在該方法內部在調用clone方法。
下劃線樣式的具體原型,實現了Product接口
,用於實現複製現有實例並生成新實例的方法。UnderlinePen類
的實現幾乎和 MessageBox類
同樣,不一樣的可能只是use方法
的實現。
package prototype_pattern; public class UnderlinePen implements Product { private char ulchar; public UnderlinePen(char ulchar) { this.ulchar = ulchar; } @Override public void use(String s) { int length = s.getBytes().length; System.out.println("\""+s+"\""); for (int i = 0; i <length+2; i++) { System.out.print(ulchar); } System.out.println(""); } @Override public Product creatClone() { Product p=null; try { p=(Product) clone(); } catch (CloneNotSupportedException e) { e.printStackTrace(); } return p; } }
Main類
首先生成Manager實例。接着,在Manager實例
中經過`register方法
註冊了UnderlinePen類
的實例(帶名字)和MessageBox類
的實例(帶名字)。
package prototype_pattern; public class Main { public static void main(String[] args) { Manager manager = new Manager(); UnderlinePen underlinePen=new UnderlinePen('~'); MessageBox mbox=new MessageBox('*'); MessageBox sbox=new MessageBox('/'); manager.register("Strong message", underlinePen); manager.register("Waring Box", mbox); manager.register("Slash Box", sbox); Product p1=manager.create("Strong message"); p1.use("hello world"); Product p2=manager.create("Waring Box"); p2.use("hello world"); Product p3=manager.create("Slash Box"); p3.use("hello world"); } }
運行結果:
經過上面的例子,相信你們對於原型模式有了更進一步的認識,下面咱們看看原型模式的幾個登場角色。
Product角色負責定義用於複製現有實例來生成新實例的方法。在示例程序中的Product接口就是該角色。
ConcretePrototype角色負責實現複製現有實例並生成新實例的方法。在示例程序中,MessageBox和UnderlinePen都是該角色。
Client角色負責使用複製實例的方法生成新的實例。在示例程序中,Manager類扮演的就是該角色。
Prototype模式的類圖:
(1) 原型模式應用於不少軟件中,若是每次建立一個對象要花大量時間,原型模式是最好的解決方案。不少軟件提供的複製(Ctrl + C)
和粘貼(Ctrl + V)
操做就是原型模式的應用,複製獲得的對象與原型對象是兩個類型相同但內存地址不一樣的對象,經過原型模式能夠大大提升對象的建立效率。
(2) 在Struts2
中爲了保證線程的安全性,Action對象
的建立使用了原型模式,訪問一個已經存在的`Action對象
時將經過克隆的方式建立出一個新的對象,從而保證其中定義的變量無須進行加鎖實現同步,每個Action
中都有本身的成員變量,避免Struts1
因使用單例模式而致使的併發和同步問題。
(3) 在Spring
中,用戶也能夠採用原型模式來建立新的bean實例
,從而實現每次獲取的是經過克隆生成的新實例,對其進行修改時對原有實例對象不形成任何影響。
本文主要介紹了:什麼是原型模式、原型模式的優缺點以及使用場景。另外,簡單介紹了深拷貝和淺拷貝以及原型模式的實際應用案例。
參考:
《圖解設計模式》
歡迎關注個人微信公衆號:" Java面試通關手冊"(一個有溫度的微信公衆號,無廣告,單純技術分享,期待與你共同進步~~~堅持原創,分享美文,分享各類Java學習資源。)
最後,就是使用阿里雲服務器一段時間後,感受阿里雲真的很不錯,就申請作了阿里雲大使,而後這是個人優惠券地址.