亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb

首頁 > 學院 > 開發設計 > 正文

Java實現單例的難點

2019-11-14 14:56:22
字體:
來源:轉載
供稿:網友

有簡單又高效的方法可以實現單例模式,但沒有一種方式能在任何情況下都確保單例的完整性。

單例模式是指某個類只被實例化一次,用來表示全局或系統范圍的組件。單例模式常用于日志記錄、工廠、窗口管理器和平臺組件管理等。我認為要盡量避免使用單例模式,因為一旦實現就很難改變或重載,而且會造成編寫測試用例困難、代碼結構糟糕等問題。另外,下面文章中的單例模式是不安全的。

人們花大量的精力研究怎樣更好地實現單例模式,但有一種簡單高效的實現方法。然而,沒有一種方法能在任何情況下都確保單例的完整性。閱讀下文,看看你是否認同。

Final字段

這種方法將構造函數私有化,向外提供一個公有的static final對象:

1
2
3
4
5
public class FooSingleton {
    public final static FooSingleton INSTANCE = new FooSingleton();
    PRivate FooSingleton() { }
    public void bar() { }
}

類加載時,static對象被初始化,此時私有的構造函數被第一次也是最后一次調用。即使在類初始化前有多個線程調用此類,JVM也能保證線程繼續運行時該類已完整初始化。然而,使用反射和setaccessible(true)方法,可以創建其他新的實例:

1
2
3
4
Constructor[] constructors = FooSingleton.class.getDeclaredConstructors();
Constructor constructor = constructors[0];
constructor.setAccessible(true);
FooSingleton spuriousFoo = (FooSingleton) constructor.newInstance(new Object[0]);

我們需要修改構造函數,使其免于多次調用,例如當它被再次調用時拋出異常。如下這樣修改FooSingleton構造函數,可以防范此類攻擊:

1
2
3
4
5
6
7
8
9
10
11
12
public class FooSingleton2 {
    private static boolean INSTANCE_CREATED;
    public final static FooSingleton2 INSTANCE = new FooSingleton2();
    private FooSingleton2() {
        if (INSTANCE_CREATED) {
            throw new IllegalStateException("You must only create one instance of this class");
        } else {
            INSTANCE_CREATED = true;
        }
    }
    public void bar() { }
}

這樣看起來安全一些了,但其實要創建新的實例還是一樣容易。我們只需修改INSTANCE_CREATED字段,再玩同樣的把戲就可以了:

1
2
3
4
5
6
7
Field f = FooSingleton2.class.getDeclaredField("INSTANCE_CREATED");
f.setAccessible(true);
f.set(null, false);
Constructor[] constructors = FooSingleton2.class.getDeclaredConstructors();
Constructor constructor = constructors[0];
constructor.setAccessible(true);
FooSingleton2 spuriousFoo = (FooSingleton2) constructor.newInstance(new Object[0]);

我們采取的任何防范措施都可能被繞過,所以此方案并不可行。

靜態工廠

使用這種方法,公有的成員類似靜態工廠:

1
2
3
4
5
6
public class FooSingleton3 {
    public final static FooSingleton3 INSTANCE = new FooSingleton3();
    private FooSingleton3() { }
    public static FooSingleton3 getInstance() { return INSTANCE; }
    public void bar() { }
}

getInstance()方法返回的永遠是同一個對象引用。雖然這個方案也無法防范反射,但還是有它的一些優點。例如,可以在不改變API的情況下,改變單例的實現。getInstance()出現在幾乎所有的單例實現中,它也標志著這真的是一個單例模式。

延遲加載的單例模式

(譯者注:在軟件工程中,Initialization-on-demand holder 這個習語指的就是延遲加載的單例模式,參見維基百科)

如果希望盡可能延遲單例的創建(懶漢式加載),可以使用延遲初始化方法,當getInstance()方法第一次調用時線程安全地創建單例。相比之前的方案當第一次引用該類時就創建單例(餓漢式加載),這是一個進步。如下:

1
2
3
4
5
6
7
8
9
10
public class FooSingleton4 {
    private FooSingleton4() {
    }
    public static FooSingleton4 getInstance() {
        return FooSingleton4Holder.INSTANCE;
    }
    private static class FooSingleton4Holder {
        private static final FooSingleton4 INSTANCE = new FooSingleton4();
    }
}

要小心序列化

如果單例實現了序列化,它就要面臨另一個威脅。因此需要將所有字段聲明為transient(這樣它就不會被序列化)并提供一個自定義的readResolve()方法返回唯一實例INSTANCE的引用。

枚舉

這里用枚舉作為單例INSTANCE的容器:

1
2
3
4
5
public enum FooEnumSingleton {
    INSTANCE;
    public static FooEnumSingleton getInstance() { return INSTANCE; }
    public void bar() { }
}

根據java語言規范8.9,“Enum的final克隆方法保證枚舉永遠無法被克隆,其特殊的序列化機制保證無法反序列化得到拷貝的對象。同時,還禁止利用反射對枚舉進行實例化。保證了這四個方面,在枚舉常量之外,就不會有其他同類的枚舉實例存在。”

這樣,我們似乎很簡單地就防范了序列化、克隆和反射的攻擊。第一次看到這段話,我立刻想要證明它是錯的。如下代碼所示,繞過這些保護是很容易的:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
Constructor con = FooEnumSingleton.class.getDeclaredConstructors()[0];
 Method[] methods = con.getClass().getDeclaredMethods();
 for (Method method : methods) {
     if (method.getName().equals("acquireConstructorAccessor")) {
         method.setAccessible(true);
         method.invoke(con, new Object[0]);
     }
  }
  Field[] fields = con.getClass().getDeclaredFields();
  Object ca = null;
  for (Field field : fields) {
      if (field.getName().equals("constructorAccessor")) {
          field.setAccessible(true);
          ca = field.get(con);
      }
  }
  Method method = ca.getClass().getMethod("newInstance", new Class[]{Object[].class});
  method.setAccessible(true);
  FooEnumSingleton spuriousEnum = (FooEnumSingleton) method.invoke(ca, new Object[]{new Object[]{"SPURIOUS_INSTANCE", 1}});
  printInfo(FooEnumSingleton.INSTANCE);
  printInfo(spuriousEnum);
}
private static void printInfo(FooEnumSingleton e) {
    System.out.println(e.getClass() + ":" + e.name() + ":" + e.ordinal());
}

執行這段代碼,得到結果:

1
2
class com.blogspot.minborgsjavapot.singleton.FooEnumSingleton:INSTANCE:0
class com.blogspot.minborgsjavapot.singleton.FooEnumSingleton:SPURIOUS_INSTANCE:1

枚舉的缺點是它無法從另一個基類繼承,因為它已經繼承自java.lang.Enum。如果想要模擬這種繼承,可以參考我另一篇文章中介紹的混入模式(mixin pattern)。

枚舉的一個優點是,如果你之后希望有“二例(dualton)”或“三例(tringleton)”,只需要增加新的枚舉實例即可。例如,有了一個單例的緩存之后,你也許還想給緩存引入多個層。

前兩天在一群里看見有人推薦一個app叫問啊,就可以發題答題那種的,感覺就跟uber滴滴打車似的,一般這種軟件一上來就砸錢給紅包啥的,哥之前刷過uber的單有經驗!試驗了幾次應該可以刷,把注冊紅包和之前領的紅包錢套現,目前我提了五十多,目測還能刷更多。ps,但是盡量要問技術相關的問題,不然容易被封。

  有技術的可以自己試,不會的可以q我:QQ群290551701


發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb
中文国产成人精品| 亚洲欧美精品一区| 精品久久久久久久久久久久| 国产午夜精品美女视频明星a级| 久久99青青精品免费观看| 久久777国产线看观看精品| 国产精品福利在线观看网址| 欧美专区中文字幕| 尤物99国产成人精品视频| 国产日韩在线精品av| 日韩一区二区在线视频| 中文字幕欧美在线| 2021久久精品国产99国产精品| 亚洲国产精品成人精品| 91亚洲精品久久久| 国产精品扒开腿做| 92裸体在线视频网站| 色香阁99久久精品久久久| 亚洲jizzjizz日本少妇| 国产成人免费av电影| 亚洲视频999| 国产精品永久免费| 久久久av一区| 国内精品一区二区三区| 亚洲男人的天堂在线播放| 亚洲肉体裸体xxxx137| 在线观看欧美日韩| 亚洲精品小视频在线观看| 欧美一区二区.| 亚洲一区二区精品| 日韩黄色高清视频| 亚洲欧洲免费视频| 欧美激情精品久久久| 性色av香蕉一区二区| 国产精品日韩在线一区| 亚洲男人天堂网| 亚洲v日韩v综合v精品v| 亚洲成人av中文字幕| 91高清免费视频| 美日韩丰满少妇在线观看| 欧美日韩免费观看中文| 久久久人成影片一区二区三区观看| 中文字幕亚洲欧美一区二区三区| 庆余年2免费日韩剧观看大牛| 啪一啪鲁一鲁2019在线视频| 久久久久亚洲精品国产| 疯狂做受xxxx高潮欧美日本| 亚洲午夜国产成人av电影男同| 欧美激情三级免费| 国产精品久久电影观看| 日本不卡高字幕在线2019| 欧美成人一区二区三区电影| 激情成人中文字幕| 欧美日韩福利在线观看| 91免费精品国偷自产在线| 91视频88av| 性色av一区二区咪爱| 97av在线播放| 最近2019中文字幕mv免费看| 国产在线观看一区二区三区| 国产91|九色| 精品视频在线播放免| 国产精品女人久久久久久| 欧洲精品在线视频| 国产v综合ⅴ日韩v欧美大片| 欧洲亚洲免费视频| 久久琪琪电影院| 2023亚洲男人天堂| 国产一区二区三区在线观看视频| 亚洲欧美中文日韩在线| 日韩精品高清视频| 亚洲国产成人在线播放| 亚洲2020天天堂在线观看| 亚洲开心激情网| 欧美国产日韩中文字幕在线| 国产欧美va欧美va香蕉在| 欧美性视频精品| 国内揄拍国内精品少妇国语| 国产成人精品在线播放| 日韩电影中文字幕| 欧洲美女7788成人免费视频| 亚洲精品成人网| 精品亚洲男同gayvideo网站| 中文字幕日韩欧美在线| 精品福利在线看| 少妇高潮久久久久久潘金莲| 欧美一级高清免费播放| 国语自产精品视频在线看一大j8| 亚洲新声在线观看| 国产精品96久久久久久| 国内免费久久久久久久久久久| 亚洲一区二区三区视频| 欧美日韩一区二区在线播放| 在线视频精品一| 亚洲成人网av| 国产999在线观看| 欧美老妇交乱视频| 亚洲日本中文字幕免费在线不卡| 久久人人爽人人| 久久好看免费视频| 欧美成人网在线| 久久精品男人天堂| 国产精品一区二区av影院萌芽| 国精产品一区一区三区有限在线| 98精品国产自产在线观看| 97久久久免费福利网址| 欧美风情在线观看| 国内精品在线一区| 欧美三级欧美成人高清www| 韩国欧美亚洲国产| 国产精品久久久久久久久久ktv| 欧美成人久久久| 色综合伊人色综合网站| 亚洲色图15p| 欧美精品免费在线| 国产中文日韩欧美| 欧美激情视频一区二区三区不卡| 91在线视频免费| 亚洲欧美国产一本综合首页| 久久青草福利网站| 欧美日韩国产在线| 亚洲欧美资源在线| 亚洲国产精品久久91精品| 亚洲最新视频在线| 日韩成人激情在线| 国产女人18毛片水18精品| 亚洲精品一区二区三区不| 久久精品中文字幕免费mv| 97成人在线视频| 亚洲高清免费观看高清完整版| 国产91色在线|| 欧美影院在线播放| 91视频国产一区| 欧美电影免费观看| 欧美激情视频网站| 九九热精品视频在线播放| 亚洲a在线播放| 成人国产在线激情| 久久影院资源网| 午夜精品久久久久久久白皮肤| 亚洲一级一级97网| 国产精品国产三级国产aⅴ浪潮| 国产美女精品免费电影| 国产suv精品一区二区三区88区| 亚洲精品成人久久| 欧美精品福利视频| 91免费看片在线| 久久久精品影院| 亚洲免费伊人电影在线观看av| 日韩精品小视频| 人人澡人人澡人人看欧美| 欧美成年人视频网站欧美| 亚洲女人天堂色在线7777| 亚洲成年人影院在线| 超碰精品一区二区三区乱码| 国产精品第七十二页| 国产一区二区三区18| 亚洲综合中文字幕68页| 亚洲国产第一页| 精品国产一区二区三区久久| 亚洲人成在线观看网站高清| 5566日本婷婷色中文字幕97| 黑人与娇小精品av专区| 久久99热这里只有精品国产|