Java-原型模式

模式定義:

使用原型實(shí)例指定創(chuàng)建對象的種類,并且通過克隆這些原型創(chuàng)建新的對象。原型模式是一種對象創(chuàng)建型模式。

使用場景:

1传藏、在你希望創(chuàng)建一個對象的時候,不僅僅想要克隆原型實(shí)例對象的基礎(chǔ)結(jié)構(gòu),還需要他當(dāng)前的對象數(shù)據(jù)毯侦。

2哭靖、對于克隆的對象的操作并不影響原型實(shí)例對象。

以克隆層次來區(qū)分侈离,可有深克隆和淺克隆兩種试幽。

淺克隆實(shí)現(xiàn)如下:

  public class PrototypeInstance implements Cloneable{

    private Integer id;
    
    private String name;
    
    private String content;
    
    private Date createDate;
    
    private ArrayList<Integer> titles = new ArrayList<>();
    
    public void addTitles(Integer value) {
        this.titles.add(value);
    }
    
    // 重寫clone方法,clone來源于Object類
    @Override
    protected PrototypeInstance clone() {
        try {
            PrototypeInstance entity = (PrototypeInstance) super.clone();
            entity.setName(getName());
            entity.setContent(getContent());
            return entity;
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
        }
        return null;
    }
        // get / set method 自行補(bǔ)齊
}


  @Test
  public void CloneInstanceTest() {

      String content = new String("當(dāng)一個種花家");

      Integer id = 1;

      Date date = new Date(1565515734562L);

      PrototypeInstance instance = new PrototypeInstance();

      instance.setId(id);

      instance.setName("花花");

      instance.setContent(content);

      instance.setCreateDate(date);

      instance.addTitles(1);

      instance.addTitles(2);

      PrototypeInstance cloneInstance = instance.clone();
        
      System.out.println("克隆" + instance.toString());
      System.out.println("克隆" + cloneInstance.toString());
  }

運(yùn)行結(jié)果:


image.png

淺克隆就是拷貝一份原型實(shí)例對象的所有字段卦碾,并把字段中的引用也拷貝了铺坞。
那么它會暴露什么問題尼?
程序總要修改對象的屬性值的吧洲胖,那我們就來試試吧济榨,如下:

public class CloneTest {

    @Test
    public void CloneInstanceTest() {
        String content = new String("當(dāng)一個種花家");
        Integer id = 1;
        Date date = new Date(1565515734562L);
        PrototypeInstance instance = new PrototypeInstance();
        instance.setId(id);
        instance.setName("花花");
        instance.setContent(content);
        instance.setCreateDate(date);
        instance.addTitles(1);
        instance.addTitles(2);

        PrototypeInstance cloneInstance = instance.clone();
        
        System.out.println("克隆" + instance.toString());
        System.out.println("克隆" + cloneInstance.toString());
        
        // 克隆完后,我突然想修改原型實(shí)例對象的值宾濒。
        System.out.println("---------- 嘗試修改原型實(shí)例對象值 -----------");

        id = 2;
        content = new String("一顆中國心");
        date.setTime(1365215478956L);
        instance.setId(id);
        instance.addTitles(3);
        instance.setContent(content);

        System.out.println("克隆" + instance.toString());
        System.out.println("克隆" + cloneInstance.toString());

    }
}

運(yùn)行的結(jié)果:

image.png

從3 4 中看出來,怎么克隆對象的屬性值也變了屏箍?1 2 且沒有變化绘梦。
TIM圖片20180819183326.png

是否可以推斷一下:
Integer / String 等字段引用類型,在原型實(shí)例對象修改字段值后克隆對象并沒有產(chǎn)生變化赴魁,但是 Date / ArrayList 類型的字段在原型實(shí)例對象修改完值后克隆對象中它的字段值卻變了卸奉。

深入源碼了解下的話,你會發(fā)現(xiàn) Date / ArrayList 類型

image.png

image.png

它們都是實(shí)現(xiàn)了Cloneable接口以及使用了transient關(guān)鍵字

transient關(guān)鍵字:java語言的關(guān)鍵字颖御,變量修飾符榄棵,如果用transient聲明一個實(shí)例變量當(dāng)對象存儲時潘拱,它的值不需要維持疹鳄。換句話來說就是,用transient關(guān)鍵字標(biāo)記的成員變量不參與序列化過程芦岂。所以在進(jìn)行對象克隆的時候瘪弓,Date / ArrayList 類型并沒有在序列化中進(jìn)行引用拷貝,僅僅是拷貝存儲值禽最,然而clone的時候 它們會進(jìn)行值的拷貝腺怯。

結(jié)論: 因?yàn)闇\度克隆對引用類型的字段進(jìn)行的是拷貝了指向?qū)ο蟮囊茫绢愋褪菍χ颠M(jìn)行備份川无。

那么如何解決這個問題尼呛占?

也就是我們接下來要說的深克隆了。

解決方式1: 就如剛才看到的源碼一樣 它們兩個是實(shí)現(xiàn)了Cloneable接口的懦趋,所以肯定是有重寫clone()方法啦晾虑。源碼里是這樣的:

image.png

image.png
它們都會返回一個克隆實(shí)例對象

所以修改原性實(shí)例對象 clone方法里面的拷貝字段就可以做到了:


image.png

運(yùn)行的結(jié)果:


image.png

完全正確,原性對象 和 克隆對象兩者互不干預(yù),不相互影響走贪,你修改你的佛猛,我寫我的

解決方式2: 直接使用序列化和反序列化來拷貝唄,代碼如下:

public class PrototypeInstance implements Cloneable,Serializable{

    private Integer id;
....

 public class CloneTest {

    @Test
    public void CloneInstanceTest() throws IOException, ClassNotFoundException {
        String content = new String("當(dāng)一個種花家");
        Integer id = 1;
        Date date = new Date(1565515734562L);
        PrototypeInstance instance = new PrototypeInstance();
        instance.setId(id);
        instance.setName("花花");
        instance.setContent(content);
        instance.setCreateDate(date);
        instance.addTitles(1);
        instance.addTitles(2);

        // PrototypeInstance cloneInstance = instance.clone();
        // 利用序列化和反序列化進(jìn)行克隆
        ByteArrayOutputStream bos = new ByteArrayOutputStream();
        ObjectOutputStream oos = new ObjectOutputStream(bos);
        oos.writeObject(instance);
        ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());
        ObjectInputStream ois = new ObjectInputStream(bis);
        PrototypeInstance cloneInstance = (PrototypeInstance) ois.readObject();
        
        System.out.println("克隆" + instance.toString());
        System.out.println("克隆" + cloneInstance.toString());
        
        // 克隆完后坠狡,我突然想修改原型實(shí)例對象的值继找。
        System.out.println("---------- 嘗試修改原型實(shí)例對象值 -----------");

        id = 2;
        content = new String("一顆中國心");
        date.setTime(1365215478956L);
        instance.setId(id);
        instance.addTitles(3);
        instance.setContent(content);

        System.out.println("克隆" + instance.toString());
        System.out.println("克隆" + cloneInstance.toString());

    }
}

優(yōu)點(diǎn)方面

原型模式是在內(nèi)存二進(jìn)制流的拷貝,創(chuàng)建對象時并不會調(diào)用類的構(gòu)造方法逃沿,所以要比直接new一個對象性能好很多婴渡,如果是大量的new 對象的話 這種性能顯現(xiàn)的特別明顯。
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末凯亮,一起剝皮案震驚了整個濱河市边臼,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌假消,老刑警劉巖柠并,帶你破解...
    沈念sama閱讀 222,252評論 6 516
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異富拗,居然都是意外死亡臼予,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,886評論 3 399
  • 文/潘曉璐 我一進(jìn)店門啃沪,熙熙樓的掌柜王于貴愁眉苦臉地迎上來粘拾,“玉大人,你說我怎么就攤上這事创千$止停” “怎么了?”我有些...
    開封第一講書人閱讀 168,814評論 0 361
  • 文/不壞的土叔 我叫張陵追驴,是天一觀的道長械哟。 經(jīng)常有香客問我,道長殿雪,這世上最難降的妖魔是什么戒良? 我笑而不...
    開封第一講書人閱讀 59,869評論 1 299
  • 正文 為了忘掉前任,我火速辦了婚禮冠摄,結(jié)果婚禮上糯崎,老公的妹妹穿的比我還像新娘。我一直安慰自己河泳,他們只是感情好沃呢,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,888評論 6 398
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著拆挥,像睡著了一般薄霜。 火紅的嫁衣襯著肌膚如雪某抓。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 52,475評論 1 312
  • 那天惰瓜,我揣著相機(jī)與錄音否副,去河邊找鬼。 笑死崎坊,一個胖子當(dāng)著我的面吹牛备禀,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播奈揍,決...
    沈念sama閱讀 41,010評論 3 422
  • 文/蒼蘭香墨 我猛地睜開眼曲尸,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了男翰?” 一聲冷哼從身側(cè)響起另患,我...
    開封第一講書人閱讀 39,924評論 0 277
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎蛾绎,沒想到半個月后昆箕,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 46,469評論 1 319
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡租冠,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,552評論 3 342
  • 正文 我和宋清朗相戀三年鹏倘,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片肺稀。...
    茶點(diǎn)故事閱讀 40,680評論 1 353
  • 序言:一個原本活蹦亂跳的男人離奇死亡第股,死狀恐怖应民,靈堂內(nèi)的尸體忽然破棺而出话原,到底是詐尸還是另有隱情,我是刑警寧澤诲锹,帶...
    沈念sama閱讀 36,362評論 5 351
  • 正文 年R本政府宣布繁仁,位于F島的核電站,受9級特大地震影響归园,放射性物質(zhì)發(fā)生泄漏黄虱。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 42,037評論 3 335
  • 文/蒙蒙 一庸诱、第九天 我趴在偏房一處隱蔽的房頂上張望捻浦。 院中可真熱鬧,春花似錦桥爽、人聲如沸朱灿。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,519評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽盗扒。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間侣灶,已是汗流浹背甸祭。 一陣腳步聲響...
    開封第一講書人閱讀 33,621評論 1 274
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留褥影,地道東北人池户。 一個月前我還...
    沈念sama閱讀 49,099評論 3 378
  • 正文 我出身青樓,卻偏偏與公主長得像伪阶,于是被迫代替她去往敵國和親煞檩。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,691評論 2 361

推薦閱讀更多精彩內(nèi)容

  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理栅贴,服務(wù)發(fā)現(xiàn)斟湃,斷路器,智...
    卡卡羅2017閱讀 134,711評論 18 139
  • 《你的燈亮著嗎檐薯?》看完后我在家門上貼了一張紙條“你的鑰匙帶了嗎凝赛?” 《你的燈亮著嗎?》這樣書名...
    柳知否閱讀 250評論 0 1
  • 晚上給女兒洗顏料抹布坛缕,洗了無數(shù)盆褐色的臟水后墓猎,又洗出了無數(shù)盆粉餅色。終于赚楚,我沒了耐心毙沾,我感覺這抹布是可以洗凈的,只...
    平淡也是渡過閱讀 828評論 0 0
  • 最初從羅胖推薦的“登高四書”我與李善友教授結(jié)緣宠页,那四本書我都不記得自己翻了多少遍左胞,第一次感覺到醍醐灌頂之感,后來加...
    孫凌聊校園閱讀 519評論 0 12
  • 從0到1萬字躺枕,這是2017年連續(xù)寫作16天的成果。 如果在過去供填,這對我就是天文數(shù)字拐云,遙不可及啊。怎么也得寫上一年半...
    一人獨(dú)占一江水閱讀 356評論 6 5