原型模式(深克隆、浅克隆)
上期回顧:建造者模式
文章目錄
- 一、原型模式定義
- 二、原型模式的結構與實現
- 三、案例
- (一)淺克隆
- (二)深克隆
- (三)淺克隆和深克隆的區別
- 四、原型模式使用場景
一、原型模式定義
原型模式是一種創建型設計模式,Prototype模式允許一個對象再創建另外一個可定制的對象,根本無需知道任何如何創建的細節,工作原理是:通過將一個原型對象傳給那個要發動創建的對象,這個要發動創建的對象通過請求原型對象拷貝它們自己來實施創建。
二、原型模式的結構與實現
原型模式有以下三種角色:
1.抽象原型類(Prototype):規定了具體原型對象必須實現的接口。
2.具體原型類(Realizetype):實現抽象原型類的 clone() 方法,它是可被復制的對象。
3.訪問類(PrototypeTest):使用具體原型類中的 clone() 方法來復制新的對象
原型模式的克隆可以分為淺克隆和深克隆
淺克隆:創建一個新對象,新對象的屬性和原來對象完全相同,對于非基本類型屬性,仍指向原有屬性所指向的對象的內存地址。
深克隆:創建一個新對象,屬性中引用的其他對象也會被克隆,不再指向原有對象地址。
Java中的Object類中提供clone()方法來實現淺克隆。Cloneable接口就是抽象原型類,而實現了Cloneable接口的子實現類就是具體的原型類。
原型模式設計如下:
//在這里抽象原型類是Cloneable接口 //具體原型類 public class Realizetype implements Cloneable{public Realizetype() {System.out.println("具體原型對象已創建成功!");}@Overrideprotected Realizetype clone() throws CloneNotSupportedException {System.out.println("具體原型已復制成功!");return (Realizetype) super.clone();} } //訪問類 public class Client {public static void main(String[] args) throws CloneNotSupportedException {//創建一個原型類對象Realizetype realizetype = new Realizetype();//得到原型對象的克隆對象Realizetype clone = realizetype.clone();//查看是否是同一個對象System.out.println("原型對象和克隆的對象是否是同一個對象:"+(realizetype == clone));} }測試結果:
具體原型對象已創建成功!
具體原型已復制成功!
原型對象和克隆的對象是否是同一個對象:false
這里展示的原型模式只是一個簡單的克隆,抽象原型類就是Cloneable接口,具體原型類就是可被復制的對象,而訪問類就是使用克隆clone()來復制新的對象。
需要注意的是,單看測試結果我們會發現原型模式有以下幾個問題。
- 克隆底層不是通過new對象實現的,因為調用clone()的時候并沒有打印出Realizetype 類的構造函數中的內容。
- 原型對象和克隆的對象不是同一個對象
而且查看clone()源碼發現它是一個native本地方法,也是說它并不是由java語言實現的。
protected native Object clone() throws CloneNotSupportedException;三、案例
用原型模式來實現以下需求:
假如某學校要對這一學期的社團指導老師頒發聘書,那么我現在就可以根據獎狀模板進行克隆,用原型模式設計思想實現。
原型對象:獎狀類
//原型對象:聘書 public class Letter implements Cloneable{private String name; //指導老師姓名public String getName() {return name;}public void setName(String name) {this.name = name;}public void show(){System.out.println("茲聘請 "+name+" 老師為 xxx 社團指導老師!");}@Overrideprotected Letter clone() throws CloneNotSupportedException {return (Letter)super.clone();} }訪問類:測試
public class Test {public static void main(String[] args) throws CloneNotSupportedException {Letter letter = new Letter();//開始克隆Letter letter1 = letter.clone();//原型對象System.out.print("原型對象:");letter.setName("張三");letter.show();//克隆對象System.out.print("克隆后對象:");letter1.setName("李四");letter1.show();} }運行結果:
原型對象: 茲聘請 張三 老師為 xxx 社團指導老師!
克隆后對象: 茲聘請 李四 老師為 xxx 社團指導老師!
這個例子比較簡單好理解,現在我在這個案例中引入深克隆和淺克隆的概念。
(一)淺克隆
淺克隆:創建一個新對象,新對象的屬性和原來對象完全相同,對于非基本類型屬性,仍指向原有屬性所指向的對象的內存地址。
1、首先我再創建一個社團類,里面有一個社團名稱的屬性
//社團類 public class Association {private String name ; //社團名稱public Association(String name) {this.name = name;}public String getName() {return name;}public void setName(String name) {this.name = name;} }2、修改聘書類,加上一個社團類的對象屬性
//原型對象:聘書 public class Letter implements Cloneable{private String name; //指導老師姓名private Association association; //社團public Letter(String name, Association association) {this.name = name;this.association = association;}public String getName() {return name;}public void setName(String name) {this.name = name;}public Association getAssociation() {return association;}public void setAssociation(Association association) {this.association = association;}public void show(){System.out.println("茲聘請 "+name+" 老師為 "+association.getName()+" 社團指導老師!");}@Overrideprotected Letter clone() throws CloneNotSupportedException {return (Letter)super.clone();} }測試類:將原型對象聘書類進行克隆,然后打印出原型對象中Association引用地址和克隆后Association引用地址,判斷該引用地址是否發生改變
public class Test {public static void main(String[] args) throws CloneNotSupportedException {Association association = new Association("歌舞社");//拷貝之前的對象Letter letter = new Letter("張三",association);System.out.print("克隆前對象:");letter.show();//拷貝之后的對象Letter letter1 = letter.clone();System.out.print("克隆后對象:");letter1.show();//判斷System.out.println(letter.getAssociation() == letter1.getAssociation());System.out.println("克隆前Association對象的hashCode:"+letter.getAssociation().hashCode());System.out.println("克隆后Association對象的hashCode:"+letter1.getAssociation().hashCode());} }輸出結果:
克隆前對象:茲聘請 張三 老師為 歌舞社 社團指導老師!
克隆后對象:茲聘請 張三 老師為 歌舞社 社團指導老師!
true
克隆前Association對象的hashCode:666988784
克隆后Association對象的hashCode:666988784
結果發現,letter.getAssociation() == letter1.getAssociation()結果返回true,說明克隆前后Letter類中持有的Association引用地址沒有發生改變,加上show()內容一樣,說明屬性也沒發生改變,這明顯符合我們對淺克隆的定義。而且打印出來的hashCode值相同,同樣證明了它們是引用的同一個地址。
由于指向同一個地址,那我對克隆后的對象修改屬性,原來的原型對象中的屬性也會發生變化嗎?
測試如下,將克隆的對象中原來的張三指導的“歌舞社”改為“足球社”,再查看未克隆前的原型對象是否發生了改變。
測試結果:
原對象:
茲聘請 張三 老師為 歌舞社 社團指導老師!
克隆對象修改后再次查看原來對象:
茲聘請 張三 老師為 足球社 社團指導老師!
發現修改克隆對象中的屬性,原對象也發生了同步改變。這再一次說明了淺克隆中,會把原型對象中成員變量為引用類型的引用地址也復制給克隆對象,且此引用對象的地址是共享給原型對象和克隆對象的。
總結:淺克隆只復制指向某個對象的引用,而不復制對象本身,新舊對象還是共享同一塊內存,修改對象會改到原對象
(二)深克隆
清楚了淺克隆例子后。再來回顧一下深克隆的定義 ---- 創建一個新對象,屬性中引用的其他對象也會被克隆,不再指向原有對象地址。
這要如何實現?很簡單>
讓Association類實現Cloneable接口,重寫clone()方法
@Override protected Association clone() throws CloneNotSupportedException {return (Association) super.clone(); }修改Letter類的clone()方法,加上對association的克隆
@Override protected Letter clone() throws CloneNotSupportedException {Letter letter = (Letter)super.clone();//拷貝Association對象letter.association= this.association.clone();return letter; }測試結果:
原對象:
茲聘請 張三 老師為 歌舞社 社團指導老師!
克隆后對象:
茲聘請 張三 老師為 足球社 社團指導老師!
克隆對象修改后再次查看原來對象:
茲聘請 張三 老師為 歌舞社 社團指導老師!
經過這一頓操作后,修改克隆后對象的屬性,也不會影響到原對象,這就是深克隆,它將原型對象中的所有類型,無論是值類型還是引用類型,都復制了一份給克隆對象。
總結:深克隆對原型對象完全拷貝,但新對象跟原對象不共享內存,修改新對象不會改變原對象。
(三)淺克隆和深克隆的區別
最后用一張圖簡單的總結一下淺克隆和深克隆的區別
四、原型模式使用場景
原型模式(Prototype Pattern)是用于創建重復的對象,同時又能保證性能。這種類型的設計模式屬于創建型模式,它提供了一種創建對象的最佳方式。
它的使用場景如下:
1、資源優化場景。
2、類初始化需要消化非常多的資源,這個資源包括數據、硬件資源等。
3、性能和安全要求的場景。
4、通過new 產生一個對象需要非常繁瑣的數據準備或訪問權限,則可以使用原型模式。
5、一個對象多個修改者的場景。
6、一個對象需要提供給其他對象訪問,而且各個調用者可能都需要修改其值時,可以考慮使用原型模式拷貝多個對象供調用者使用。
7、在實際項目中,原型模式很少單獨出現,一般是和工廠方法模式一起出現,通過 clone 的方法創建一個對象,然后由工廠方法提供給調用者。原型模式已經與 Java 融為渾然一體,大家可以隨手拿來使用。
總結
以上是生活随笔為你收集整理的原型模式(深克隆、浅克隆)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 【ADS学习笔记(二)——ADS初次仿真
- 下一篇: 论文笔记:针对盲化的 RSA算法的水平聚