設(shè)計(jì)模式——單例模式的破壞

概述:

  • 之前學(xué)習(xí)了單例模式的幾種實(shí)現(xiàn)拢驾,解決了多線(xiàn)程情況下繁疤,單例的線(xiàn)程安全問(wèn)題稠腊,保證了單例的實(shí)現(xiàn)架忌。但是單例模式在下面兩種情況下也會(huì)被破壞:反射叹放、序列化许昨。

反射:

  • 通過(guò)反射是可以破壞單例的糕档,例如使用內(nèi)部類(lèi)實(shí)現(xiàn)的單例拌喉。通過(guò)反射獲取其默認(rèn)的構(gòu)造函數(shù)尿背,然后使默認(rèn)構(gòu)造函數(shù)可訪(fǎng)問(wèn)田藐,就可以創(chuàng)建新的對(duì)象了。如下面代碼:
public class MainClass {

    public static void main(String[] args) {

        PersonLazyInnerClass person1 = PersonLazyInnerClass.getPersonLazyInnerClass();
        PersonLazyInnerClass person2 = null;
        try {
            Class<PersonLazyInnerClass> cla = PersonLazyInnerClass.class;
            //獲得默認(rèn)構(gòu)造函數(shù)
            Constructor<PersonLazyInnerClass> cons = cla.getDeclaredConstructor();
            //使默認(rèn)構(gòu)造函數(shù)可訪(fǎng)問(wèn)
            cons.setAccessible(true);
            //創(chuàng)建對(duì)象
            person2 = cons.newInstance();
        }catch (Exception e){
            e.printStackTrace();
        }
        System.out.println(person1.hashCode());
        System.out.println(person2.hashCode());
    }
}
  • 測(cè)試結(jié)果


    image.png
  • 所以想要阻止破壞單例,思路是阻止外部能調(diào)用類(lèi)的構(gòu)造函數(shù)一次以上吝岭。所以可以增加一個(gè)標(biāo)志位窜管,用于判斷構(gòu)造函數(shù)是否被調(diào)用過(guò)幕帆。如下代碼:

public class PersonLazyInnerClassSafe {

    //申明一個(gè)標(biāo)志位蜓肆,用于標(biāo)志構(gòu)造函數(shù)是否被調(diào)用過(guò)
    private static Boolean alreadyNew = false;

    private PersonLazyInnerClassSafe(){
        //加鎖防止并發(fā)
        synchronized (PersonLazyInnerClassSafe.class){
            //第一次被調(diào)用仗扬,僅修改標(biāo)志位早芭;后續(xù)被調(diào)用拋異常
            if(alreadyNew == false){
                alreadyNew = true;
            }else {
                throw new RuntimeException("單例模式被破壞退个!");
            }
        }
    }

    private static class Holder{
        private static PersonLazyInnerClassSafe personLazyInnerClassSafe = new PersonLazyInnerClassSafe();
    }

    public static PersonLazyInnerClassSafe getInstance(){
        return Holder.personLazyInnerClassSafe;
    }
}
  • 測(cè)試結(jié)果


    image.png
  • 增加標(biāo)志位的確能阻止單例的破壞,但是這個(gè)代碼有一個(gè)BUG刀荒,那就是如果單例是先用的反射創(chuàng)建的缠借,那如果你再用正常的方法getInstance()獲取單例泼返,就會(huì)報(bào)錯(cuò)绅喉。因?yàn)榇藭r(shí)標(biāo)志位已經(jīng)標(biāo)志構(gòu)造函數(shù)被調(diào)用過(guò)了霹疫。這種寫(xiě)法除非你能保證getInstance先于反射執(zhí)行丽蝎。
  • 先試正常執(zhí)行代碼屠阻,此時(shí)是可以正確返回單例国觉。這里簡(jiǎn)單解釋一下麻诀,person2執(zhí)行的時(shí)候不會(huì)去調(diào)用PersonLazyInnerClassSafe類(lèi)的構(gòu)造函數(shù)蝇闭。因?yàn)镠older內(nèi)部類(lèi)里面personLazyInnerClassSafe屬性是靜態(tài)的呻引,靜態(tài)屬性在類(lèi)加載的時(shí)候就初始化一次逻悠,在生命周期內(nèi)就不會(huì)再被初始化了童谒。
public class MainClass {

    public static void main(String[] args) {
        //正常執(zhí)行代碼
        PersonLazyInnerClassSafe person1 = PersonLazyInnerClassSafe.getInstance();
        PersonLazyInnerClassSafe person2 = PersonLazyInnerClassSafe.getInstance();
        System.out.println(person1.hashCode());
        System.out.println(person2.hashCode());
    }
}
  • 先反射后正常調(diào)用饥伊,此時(shí)會(huì)發(fā)現(xiàn)正常調(diào)用沒(méi)法用了撵渡。
public class MainClass {

    public static void main(String[] args) {
        //先反射獲取單例
        PersonLazyInnerClassSafe person2 = null;
        try {
            Class<PersonLazyInnerClassSafe> cla = PersonLazyInnerClassSafe.class;
            //獲得默認(rèn)構(gòu)造函數(shù)
            Constructor<PersonLazyInnerClassSafe> cons = cla.getDeclaredConstructor();
            //使默認(rèn)構(gòu)造函數(shù)可訪(fǎng)問(wèn)
            cons.setAccessible(true);
            //調(diào)用默認(rèn)構(gòu)造函數(shù),創(chuàng)建對(duì)象
            person2 = cons.newInstance();
        }catch (Exception e){
            e.printStackTrace();
        }
        System.out.println("反射對(duì)象: " + person2.hashCode());
        //在用正常的方法獲取單例节腐,此時(shí)會(huì)報(bào)錯(cuò)
        PersonLazyInnerClassSafe person1 = PersonLazyInnerClassSafe.getInstance();
        System.out.println("正常獲取" + person1.hashCode());
    }
}
  • 測(cè)試結(jié)果


反序列化:

1. 破壞單例
  • 反序列化也是一種會(huì)破壞單例的方法饱苟。簡(jiǎn)單來(lái)講箱熬,反序列化也是通過(guò)反射調(diào)用newInstance()實(shí)例化對(duì)象城须,下面我一步步解釋糕伐。例如下面代碼可以通過(guò)反序列化破壞單例:
public class MainClass {

    public static void main(String[] args) {
        //序列化
        PersonLazyInnerClassSafe person3 = PersonLazyInnerClassSafe.getInstance();
        ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("tempfile"));
        out.writeObject(PersonLazyInnerClassSafe.getInstance());

        File file  = new File("tempfile");
        ObjectInputStream in = new ObjectInputStream(new FileInputStream(file));
        //調(diào)用readObject()反序列化
        PersonLazyInnerClassSafe person4 = (PersonLazyInnerClassSafe)in.readObject();
        System.out.println("正常構(gòu)造:" + person3.hashCode());
        System.out.println("反序列化Person:" + person4.hashCode());
    }
}
  • 測(cè)試結(jié)果
    可以看到良瞧,反序列化后褥蚯,生成了一個(gè)新的實(shí)例遵岩。


    image.png
  • 原理解釋
    反序列化為什么能生成新的實(shí)例,必須從源碼看起誊锭。這里分析readObject()里面的調(diào)用源碼丧靡。會(huì)發(fā)現(xiàn)readObject()方法后進(jìn)入了readObject0(false)方法温治。
    public final Object readObject()
        throws IOException, ClassNotFoundException
    {
        if (enableOverride) {
            return readObjectOverride();
        }

        // if nested read, passHandle contains handle of enclosing object
        int outerHandle = passHandle;
        try {
            Object obj = readObject0(false);  //通過(guò)debug會(huì)發(fā)現(xiàn)進(jìn)入此方法
            handles.markDependency(outerHandle, passHandle);
            ClassNotFoundException ex = handles.lookupException(passHandle);
            if (ex != null) {
                throw ex;
            }
            if (depth == 0) {
                vlist.doCallbacks();
            }
            return obj;
        } finally {
            passHandle = outerHandle;
            if (closed && depth == 0) {
                clear();
            }
        }
    }

分析readObject0方法,會(huì)發(fā)現(xiàn)進(jìn)入了readOrdinaryObject()方法卤恳。

    private Object readObject0(boolean unshared) throws IOException {
        boolean oldMode = bin.getBlockDataMode();
        if (oldMode) {
            int remain = bin.currentBlockRemaining();
            if (remain > 0) {
                throw new OptionalDataException(remain);
            } else if (defaultDataEnd) {
                /*
                 * Fix for 4360508: stream is currently at the end of a field
                 * value block written via default serialization; since there
                 * is no terminating TC_ENDBLOCKDATA tag, simulate
                 * end-of-custom-data behavior explicitly.
                 */
                throw new OptionalDataException(true);
            }
            bin.setBlockDataMode(false);
        }

        byte tc;
        while ((tc = bin.peekByte()) == TC_RESET) {
            bin.readByte();
            handleReset();
        }

        depth++;
        try {
            switch (tc) {
                ...省略部分源碼

                case TC_OBJECT:
                    return checkResolve(readOrdinaryObject(unshared));   //會(huì)進(jìn)入該邏輯

                case TC_EXCEPTION:
                    IOException ex = readFatalException();
                    throw new WriteAbortedException("writing aborted", ex);

                case TC_BLOCKDATA:
                case TC_BLOCKDATALONG:
                    if (oldMode) {
                        bin.setBlockDataMode(true);
                        bin.peek();             // force header read
                        throw new OptionalDataException(
                            bin.currentBlockRemaining());
                    } else {
                        throw new StreamCorruptedException(
                            "unexpected block data");
                    }

                case TC_ENDBLOCKDATA:
                    if (oldMode) {
                        throw new OptionalDataException(true);
                    } else {
                        throw new StreamCorruptedException(
                            "unexpected end of block data");
                    }

                default:
                    throw new StreamCorruptedException(
                        String.format("invalid type code: %02X", tc));
            }
        } finally {
            depth--;
            bin.setBlockDataMode(oldMode);
        }
    }
  • 通過(guò)分析若债,readOrdinaryObject()中有兩處關(guān)鍵代碼蠢琳,其中關(guān)鍵代碼1中的關(guān)鍵語(yǔ)句為:

obj = desc.isInstantiable() ? desc.newInstance() : null;

  • 此處代碼是通過(guò)描述對(duì)象desc挪凑,先判斷類(lèi)是否可以實(shí)例化,如果可以實(shí)例化散怖,則執(zhí)行desc.newInstance()通過(guò)反射實(shí)例化類(lèi)镇眷,否則返回null欠动。
   private Object readOrdinaryObject(boolean unshared)
        throws IOException
    {
        if (bin.readByte() != TC_OBJECT) {
            throw new InternalError();
        }

        ObjectStreamClass desc = readClassDesc(false);  //根據(jù)對(duì)象的格式,下一步是讀取類(lèi)描述信息  
        desc.checkDeserialize();

        Class<?> cl = desc.forClass();
        if (cl == String.class || cl == Class.class  //讀取Class
                || cl == ObjectStreamClass.class) {
            throw new InvalidClassException("invalid class descriptor");
        }
        
        /**===============關(guān)鍵代碼1====================== **/
        Object obj;
        try {
            obj = desc.isInstantiable() ? desc.newInstance() : null;  //通過(guò)類(lèi)描述信息惑申,初始化對(duì)象obj具伍。
        } catch (Exception ex) {
            throw (IOException) new InvalidClassException(
                desc.forClass().getName(),
                "unable to create instance").initCause(ex);
        }
         /**===============關(guān)鍵代碼1====================== **/
        passHandle = handles.assign(unshared ? unsharedMarker : obj);
        ClassNotFoundException resolveEx = desc.getResolveException();
        if (resolveEx != null) {
            handles.markException(passHandle, resolveEx);
        }

        if (desc.isExternalizable()) {
            readExternalData((Externalizable) obj, desc);
        } else {
            readSerialData(obj, desc);
        }

        handles.finish(passHandle);
         /**===============關(guān)鍵代碼2====================== **/
        if (obj != null &&
            handles.lookupException(passHandle) == null &&
            desc.hasReadResolveMethod())
        {
            Object rep = desc.invokeReadResolve(obj);
            if (unshared && rep.getClass().isArray()) {
                rep = cloneArray(rep);
            }
            if (rep != obj) {
                handles.setObject(passHandle, obj = rep);
            }
        }
         /**===============關(guān)鍵代碼2====================== **/
        return obj;
    }
  • 此處通過(guò)debug可以發(fā)現(xiàn),類(lèi)是可實(shí)例化的圈驼,所以執(zhí)行desc.newInstance() 去實(shí)例化對(duì)象人芽,此處就是重點(diǎn),讓我疑問(wèn)的地方了萤厅。我們知道newInstance必定會(huì)調(diào)用默認(rèn)的無(wú)參構(gòu)造函數(shù)。但是我們?cè)谏厦?em>PersonLazyInnerClassSafe單例類(lèi)已經(jīng)禁止了反射創(chuàng)建新實(shí)例靴迫,但是反序列化還是能創(chuàng)建出新實(shí)例惕味,那么此處的反序列化中的反射具體是如何執(zhí)行的呢?繼續(xù)分析newInstance()源碼玉锌。
  • newInstance()的源碼如下所示名挥,可以發(fā)現(xiàn),程序會(huì)自動(dòng)加載合適的構(gòu)造函數(shù)cons芬沉,然后再去根據(jù)這個(gè)構(gòu)造函數(shù)去cons.newInstance()創(chuàng)建對(duì)象躺同。通過(guò)debug發(fā)現(xiàn)阁猜,此處的cons對(duì)象是Object,真像大白了蹋艺。所以反序列化是通過(guò)Object的構(gòu)造函數(shù)去反射剃袍,生成了新的實(shí)例
/** serialization-appropriate constructor, or null if none */
private Constructor<?> cons;
...省略部分源碼
Object newInstance()
        throws InstantiationException, InvocationTargetException,
               UnsupportedOperationException
    {
        requireInitialized();
        if (cons != null) {
            try {
                return cons.newInstance();
            } catch (IllegalAccessException ex) {
                // should not occur, as access checks have been suppressed
                throw new InternalError(ex);
            }
        } else {
            throw new UnsupportedOperationException();
        }
    }
  • 此處可以給大家看下debug 的截圖


    image.png
  • 然后我改寫(xiě)了上面的測(cè)試序列化破壞單例的代碼捎谨,驗(yàn)證一下反序列化是通過(guò)Object的構(gòu)造函數(shù)去反射民效,生成了新的實(shí)例,具體代碼如下
public class MainClass {

    public static void main(String[] args) {
        //驗(yàn)證一下反序列化是通過(guò)Object的構(gòu)造函數(shù)去反射涛救,生成了新的實(shí)例
        PersonLazyInnerClassSafe person3 = PersonLazyInnerClassSafe.getInstance();
        ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("tempfile"));
        out.writeObject(PersonLazyInnerClassSafe.getInstance());

        File file  = new File("tempfile");
        ObjectInputStream in = new ObjectInputStream(new FileInputStream(file));
        //反序列化生成的Object對(duì)象
        Object ob = in.readObject();
        //將Object強(qiáng)轉(zhuǎn)為PersonLazyInnerClassSafe
        PersonLazyInnerClassSafe person4 = (PersonLazyInnerClassSafe)ob;
        System.out.println("正常構(gòu)造:" + person3.hashCode());
        System.out.println("反序列化Object:" + ob.hashCode());
        System.out.println("反序列化Person:" + person4.hashCode());
    }
}
  • 其中會(huì)發(fā)現(xiàn)反序列化出來(lái)的Object對(duì)象強(qiáng)制轉(zhuǎn)換為PersonLazyInnerClassSafe時(shí)畏邢,只是把ob 對(duì)象的引用指向了person4 ,并沒(méi)有調(diào)用PersonLazyInnerClassSafe 的構(gòu)造函數(shù)检吆。

//反序列化生成的Object對(duì)象
Object ob = in.readObject();
//將Object強(qiáng)轉(zhuǎn)為PersonLazyInnerClassSafe
PersonLazyInnerClassSafe person4 = (PersonLazyInnerClassSafe)ob;

  • 運(yùn)行結(jié)果如下舒萎,可以看到ob和person4 是相同的對(duì)象:


    image.png
2. 阻止破壞單例
  • 上面解釋了那么多序列化是如何通過(guò)反射破壞的單例,那么如何阻止單例被序列化破壞呢蹭沛?關(guān)鍵點(diǎn)在上面readOrdinaryObject()方法源碼的關(guān)鍵代碼2中臂寝,下面單獨(dú)截取該段代碼:
        if (obj != null &&
            handles.lookupException(passHandle) == null &&
            desc.hasReadResolveMethod())   //判斷對(duì)象是否實(shí)現(xiàn)readResolve方法  
        {
            Object rep = desc.invokeReadResolve(obj); //反射調(diào)用對(duì)象的readResolve方法  
            if (unshared && rep.getClass().isArray()) {
                rep = cloneArray(rep);
            }
            if (rep != obj) {  //如果對(duì)象readResolve返回的對(duì)象與 默認(rèn)序列化對(duì)象不等,返回readResolve方法返回的對(duì)象  
                handles.setObject(passHandle, obj = rep);
            }
        }
  • 通過(guò)閱讀上面代碼的注釋?zhuān)覀兗纯芍捞穑恍枰獙?shí)現(xiàn)我們自己的readResolve()方法咆贬,在這個(gè)方法中返回我們想要的單例,就可以解決序列化破壞單例帚呼。具體改寫(xiě)如下:
public class PersonLazyInnerClassSafe implements Serializable{

    //申明一個(gè)標(biāo)志位掏缎,用于標(biāo)志構(gòu)造函數(shù)是否被調(diào)用過(guò)
    public static Boolean alreadyNew = false;

    private PersonLazyInnerClassSafe(){
        System.out.println("調(diào)用構(gòu)造函數(shù)!C荷薄眷蜈!");
        //加鎖防止并發(fā)
        synchronized (PersonLazyInnerClassSafe.class){
            //第一次被調(diào)用,僅修改標(biāo)志位怜珍;后續(xù)被調(diào)用拋異常
            if(alreadyNew == false){
                alreadyNew = true;
            }else {
                throw new RuntimeException("單例模式被破壞端蛆!");
            }
        }
    }

    private static class Holder{
        private static PersonLazyInnerClassSafe personLazyInnerClassSafe = new PersonLazyInnerClassSafe();
    }

    public static PersonLazyInnerClassSafe getInstance(){
        return Holder.personLazyInnerClassSafe;
    }

    //阻止序列化破壞單例
    private Object readResolve(){
        return Holder.personLazyInnerClassSafe;
    }
}
  • 測(cè)試如下,成功解決單例被破壞問(wèn)題


    image.png

總結(jié):

  1. 破壞單例有兩種方式 反射酥泛、反序列化
  2. 反射破壞的原理是:通過(guò)反射獲取其默認(rèn)的構(gòu)造函數(shù)今豆,并且改變其構(gòu)造函數(shù)的訪(fǎng)問(wèn)域,從而實(shí)現(xiàn)調(diào)用構(gòu)造函數(shù)創(chuàng)建新實(shí)例柔袁。解決方案是:在構(gòu)造函數(shù)中增加一個(gè)標(biāo)志位呆躲,用于判斷構(gòu)造函數(shù)是否被調(diào)用過(guò),阻止外部能調(diào)用類(lèi)的構(gòu)造函數(shù)一次以上捶索。
  3. 反序列化破壞構(gòu)造函數(shù)的原理:通過(guò)Object的構(gòu)造函數(shù)插掂,反射出單例類(lèi)對(duì)象,從而創(chuàng)建了新的實(shí)例。解決方案是:在單例類(lèi)中寫(xiě)一個(gè)readResolve()方法辅甥,在這個(gè)方法中返回我們想要的單例酝润,就可以解決序列化破壞單例。
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末璃弄,一起剝皮案震驚了整個(gè)濱河市要销,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌夏块,老刑警劉巖疏咐,帶你破解...
    沈念sama閱讀 222,104評(píng)論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異脐供,居然都是意外死亡浑塞,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,816評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門(mén)政己,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)酌壕,“玉大人,你說(shuō)我怎么就攤上這事歇由〗龊ⅲ” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 168,697評(píng)論 0 360
  • 文/不壞的土叔 我叫張陵印蓖,是天一觀(guān)的道長(zhǎng)。 經(jīng)常有香客問(wèn)我京腥,道長(zhǎng)赦肃,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 59,836評(píng)論 1 298
  • 正文 為了忘掉前任公浪,我火速辦了婚禮他宛,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘欠气。我一直安慰自己厅各,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,851評(píng)論 6 397
  • 文/花漫 我一把揭開(kāi)白布预柒。 她就那樣靜靜地躺著队塘,像睡著了一般。 火紅的嫁衣襯著肌膚如雪宜鸯。 梳的紋絲不亂的頭發(fā)上憔古,一...
    開(kāi)封第一講書(shū)人閱讀 52,441評(píng)論 1 310
  • 那天,我揣著相機(jī)與錄音淋袖,去河邊找鬼鸿市。 笑死,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的焰情。 我是一名探鬼主播陌凳,決...
    沈念sama閱讀 40,992評(píng)論 3 421
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼内舟!你這毒婦竟也來(lái)了合敦?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 39,899評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤谒获,失蹤者是張志新(化名)和其女友劉穎蛤肌,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體批狱,經(jīng)...
    沈念sama閱讀 46,457評(píng)論 1 318
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡裸准,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,529評(píng)論 3 341
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了赔硫。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片炒俱。...
    茶點(diǎn)故事閱讀 40,664評(píng)論 1 352
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖爪膊,靈堂內(nèi)的尸體忽然破棺而出权悟,到底是詐尸還是另有隱情,我是刑警寧澤推盛,帶...
    沈念sama閱讀 36,346評(píng)論 5 350
  • 正文 年R本政府宣布峦阁,位于F島的核電站,受9級(jí)特大地震影響耘成,放射性物質(zhì)發(fā)生泄漏榔昔。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 42,025評(píng)論 3 334
  • 文/蒙蒙 一瘪菌、第九天 我趴在偏房一處隱蔽的房頂上張望撒会。 院中可真熱鬧,春花似錦师妙、人聲如沸诵肛。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 32,511評(píng)論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)怔檩。三九已至丐箩,卻和暖如春澎语,著一層夾襖步出監(jiān)牢的瞬間掀亥,已是汗流浹背梭伐。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,611評(píng)論 1 272
  • 我被黑心中介騙來(lái)泰國(guó)打工太抓, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留从藤,地道東北人罪塔。 一個(gè)月前我還...
    沈念sama閱讀 49,081評(píng)論 3 377
  • 正文 我出身青樓漓摩,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親膊爪。 傳聞我的和親對(duì)象是個(gè)殘疾皇子自阱,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,675評(píng)論 2 359

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

  • 單例模式(SingletonPattern)一般被認(rèn)為是最簡(jiǎn)單、最易理解的設(shè)計(jì)模式米酬,也因?yàn)樗暮?jiǎn)潔易懂沛豌,是項(xiàng)目中最...
    成熱了閱讀 4,257評(píng)論 4 34
  • 前言 本文主要參考 那些年,我們一起寫(xiě)過(guò)的“單例模式”赃额。 何為單例模式加派? 顧名思義,單例模式就是保證一個(gè)類(lèi)僅有一個(gè)...
    tandeneck閱讀 2,517評(píng)論 1 8
  • 在Java中跳芳,單例模式分為很多種芍锦,本人所了解的單例模式有以下幾種,如有不全還請(qǐng)大家留言指點(diǎn): 餓漢式 懶漢式/Do...
    Duang了個(gè)Duang閱讀 1,402評(píng)論 0 2
  • 隧道里暗風(fēng)涌動(dòng)飞盆,我聞到了她身上的味道娄琉。 汗汗的夾雜著香味,我默默的猜測(cè)她的工作吓歇。 是去鎮(zhèn)上剛賣(mài)完特產(chǎn)嗎孽水? 特產(chǎn)是從...
    由緒閱讀 157評(píng)論 0 2
  • 海南——一個(gè)我曾計(jì)劃環(huán)島騎行畢業(yè)游,卻未能成行之處城看。 上天卻也很給面子女气,老師安排了我過(guò)來(lái)這邊出差,反而能夠“公費(fèi)游...
    前行的始者007閱讀 173評(píng)論 0 1