<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          「補課」進行時:設計模式(6)——原型模式

          共 1298字,需瀏覽 3分鐘

           ·

          2020-11-02 10:15

          a1765c5a3d8edc72ee8504460c492d2d.webp

          1. 前文匯總

          「補課」進行時:設計模式系列

          2. 找工作

          這一天,郭靖大俠因為在桃花島調(diào)戲侍女被黃蓉打出了桃花島,這下可玩大了,從桃花島被趕出來吃啥喝啥啊,得趕緊找份工作,西北風可喝不飽肚子哇~~~

          這不,我們的郭大俠就開始寫簡歷,準備向丐幫、全真教、白駝山和段氏家族投一份簡歷,看看能不能先混碗飯吃,等老婆的氣消了再回去。

          首先,先定義一個簡歷類:

          public?class?Resume?{
          ????private?String?name;
          ????private?String?position;
          ????private?int?salary;

          ????//?省略?get/set

          ????@Override
          ????public?String?toString()?{
          ????????return?"Resume{"?+
          ????????????????"name='"?+?name?+?'\''?+
          ????????????????",?position='"?+?position?+?'\''?+
          ????????????????",?salary="?+?salary?+
          ????????????????'}';
          ????}
          }

          然后,我們的郭大俠開始了熬夜寫簡歷的生活:

          public?class?Test?{
          ????public?static?void?main(String[]?args)?{
          ????????Resume?resume1?=?new?Resume();
          ????????resume1.setName("小郭");
          ????????resume1.setPosition("一代大俠");
          ????????resume1.setSalary(1000);
          ????????System.out.println(resume1);

          ????????Resume?resume2?=?new?Resume();
          ????????resume2.setName("小郭");
          ????????resume2.setPosition("一代大俠");
          ????????resume2.setSalary(1200);
          ????????System.out.println(resume2);

          ????????Resume?resume3?=?new?Resume();
          ????????resume3.setName("小郭");
          ????????resume3.setPosition("一代大俠");
          ????????resume3.setSalary(1500);
          ????????System.out.println(resume3);

          ????????//?...
          }

          簡歷這么一份一份的寫太累了,工作都沒找到可能先餓死了,不行,小郭同學需要提高寫簡歷的效率,于是,他去找了一個打印機回來:

          public?class?Test?{
          ????public?static?void?main(String[]?args)?{
          ????????//?效率倍增,直接循環(huán)開始寫簡歷
          ????????for?(int?i?=?0;?i?5;?i++)?{
          ????????????Resume?resume4?=?new?Resume();
          ????????????int?salary?=?(int)(1000?+?Math.random()?*?(2000?-?1000?+?1));
          ????????????resume4.setName("小郭");
          ????????????resume4.setPosition("一代大俠");
          ????????????resume4.setSalary(salary);
          ????????????System.out.println(resume4.toString());
          ????????}
          ????}
          }

          這個時候,感覺效率好像還是有點低,每次只能一張一張打印,浪費時間,于是乎,我們的郭大俠又去搞了一個復印機回來。

          可是使用復印機需要我們原本的簡歷支持這個功能,聽過這個功能需要擴展 Cloneable 接口:

          public?class?ResumeClone?implements?Cloneable?{
          ????private?String?name;
          ????private?String?position;
          ????private?int?salary;
          ????//?省略?get/set
          ????@Override
          ????protected?ResumeClone?clone(){
          ????????ResumeClone?resumeClone?=?null;
          ????????try{
          ????????????resumeClone?=?(ResumeClone)?super.clone();
          ????????}catch?(CloneNotSupportedException?e){
          ????????????e.printStackTrace();
          ????????}
          ????????return?resumeClone;
          ????}

          ????@Override
          ????public?String?toString()?{
          ????????return?"ResumeClone{"?+
          ????????????????"name='"?+?name?+?'\''?+
          ????????????????",?position='"?+?position?+?'\''?+
          ????????????????",?salary="?+?salary?+
          ????????????????'}';
          ????}
          }

          然后我們的復印機就能跑起來了:

          public?class?TestClone?{
          ????public?static?void?main(String[]?args)?{
          ????????int?num?=?5;
          ????????ResumeClone?resumeClone?=?new?ResumeClone();
          ????????while?(num?>?0){
          ????????????ResumeClone?resume1?=?resumeClone.clone();
          ????????????int?salary?=?(int)(1000?+?Math.random()?*?(2000?-?1000?+?1));
          ????????????resume1.setName("小郭");
          ????????????resume1.setPosition("一代大俠");
          ????????????resume1.setSalary(salary);
          ????????????System.out.println(resume1.toString());
          ????????????num?--;
          ????????}
          ????}
          }

          這里實際上我們只有第一個對象是使用打印機打印出來的,后面的對象都是通過復印機直接復印出來的。

          這其實就是設計模式中的原型模式。

          3. 原型模式

          原型模式(Prototype Pattern)的簡單程度僅次于單例模式和迭代器模式。正是由于簡單,使用的場景才非常地多,其定義如下:

          Specify the kinds of objects to create using a prototypical instance,andcreate new objects by copying this prototype.(用原型實例指定創(chuàng)建對象的種類,并且通過拷貝這些原型創(chuàng)建新的對象。)

          a4de234741047ea5b5b60b3a0d54f8e1.webp

          這個絕對是最簡單的設計模式,整個模式的核心就只有一個 clone 方法,通過該方法進行對象的拷貝, Java 提供了一個 Cloneable 接口來標示這個對象是可拷貝的,為什么說是「標示」呢?翻開 JDK 的幫助看看 Cloneable 是一個方法都沒有的,這個接口只是一個標記作用,在 JVM 中具有這個標記的對象才有可能被拷貝。那怎么才能從「有可能被拷貝」轉(zhuǎn)換為「可以被拷貝」呢?方法是覆蓋 clone() 方法。

          通用代碼:

          public?class?PrototypeClass?implements?Cloneable{
          ????@Override
          ????protected?PrototypeClass?clone()?{
          ????????PrototypeClass?prototypeClass?=?null;
          ????????try?{
          ????????????prototypeClass?=?(PrototypeClass)?super.clone();
          ????????}?catch?(CloneNotSupportedException?e)?{
          ????????????e.printStackTrace();
          ????????}
          ????????return?prototypeClass;
          ????}
          }

          優(yōu)點:

          1. 性能優(yōu)良

          原型模式是在內(nèi)存二進制流的拷貝,要比直接 new 一個對象性能好很多,特別是要在一個循環(huán)體內(nèi)產(chǎn)生大量的對象時,原型模式可以更好地體現(xiàn)其優(yōu)點。

          1. 逃避構(gòu)造函數(shù)的約束

          這既是它的優(yōu)點也是缺點,直接在內(nèi)存中拷貝,構(gòu)造函數(shù)是不會執(zhí)行的。優(yōu)點就是減少了約束,缺點也是減少了約束。

          4. 構(gòu)造函數(shù)

          先看一個簡單的有關構(gòu)造函數(shù)的示例:

          public?class?ConstructorDemo?implements?Cloneable?{
          ????public?ConstructorDemo()?{
          ????????System.out.println("我被執(zhí)行了。。。");
          ????}

          ????@Override
          ????protected?ConstructorDemo?clone(){
          ????????ConstructorDemo?demo?=?null;
          ????????try?{
          ????????????demo?=?(ConstructorDemo)?super.clone();
          ????????}catch?(CloneNotSupportedException?e){
          ????????????e.printStackTrace();
          ????????}
          ????????return?demo;
          ????}
          }

          public?class?ConstructorTest?{
          ????public?static?void?main(String[]?args)?{
          ????????ConstructorDemo?demo?=?new?ConstructorDemo();
          ????????ConstructorDemo?demo1?=?demo.clone();
          ????}
          }

          執(zhí)行結(jié)果如下:

          我被執(zhí)行了。。。

          就輸出一次,這里可以證明對象拷貝的時候構(gòu)造函數(shù)是不會執(zhí)行的,原因在于拷貝是直接在堆中進行,這其實也可以理解, new 的時候, JVM 要走一趟類加載流程,這個流程非常麻煩,在類加載流程中會調(diào)用構(gòu)造函數(shù),最后生成的對象會放到堆中,而拷貝就是直接拷貝堆中的現(xiàn)成的二進制對象,然后重新一個分配內(nèi)存塊。

          5. 淺拷貝和深拷貝

          先看一個淺拷貝的案例:

          public?class?ShallowCopy?implements?Cloneable?{
          ????private?ArrayList?array?=?new?ArrayList<>?();
          ????@Override
          ????public?ShallowCopy?clone()?{
          ????????ShallowCopy?copy?=?null;
          ????????try?{
          ????????????copy?=?(ShallowCopy)?super.clone();
          ????????}?catch?(CloneNotSupportedException?e)?{
          ????????????e.printStackTrace();
          ????????}
          ????????return?copy;
          ????}

          ????public?void?setValue(String?value)?{
          ????????this.array.add(value);
          ????}

          ????public?ArrayList?getValue()?{
          ????????return?this.array;
          ????}
          }

          public?class?ShallowCopyTest?{
          ????public?static?void?main(String[]?args)?{
          ????????ShallowCopy?copy?=?new?ShallowCopy();
          ????????copy.setValue("123");
          ????????ShallowCopy?copy1?=?copy.clone();
          ????????copy1.setValue("456");
          ????????System.out.println(copy.getValue());
          ????}
          }

          執(zhí)行的結(jié)果是:

          [123,?456]

          這種情況就是淺拷貝, Java 只拷貝你指定的對象,至于你指定的對象里面的別的對象,它不拷貝,還是把引用給你,共享變量,這是一種非常不安全的方式,需要特別注意。

          內(nèi)部的數(shù)組和引用對象不會拷貝,其他的原始基本類型和 String 類型會被拷貝。

          那么這種情況如何進行一個深拷貝呢?只需要修改一下剛才 clone 的方法:

          //?深拷貝
          @Override
          public?ShallowCopy?clone()?{
          ????ShallowCopy?copy?=?null;
          ????try?{
          ????????copy?=?(ShallowCopy)?super.clone();
          ????????this.array?=?(ArrayList)?this.array.clone();
          ????}?catch?(CloneNotSupportedException?e)?{
          ????????e.printStackTrace();
          ????}
          ????return?copy;
          }

          還是剛才的測試類,這次的運行結(jié)果是:

          [123]





          感謝閱讀e38899d0136cbd1d19d4e0fd46875a36.webp



          瀏覽 17
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  欧美性爱免费在线视频PK视频 | 国产真空露出福利视频 | 老色鬼久久综合 | 亚洲AV男人天堂 | 大香蕉青青 |