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