枚舉單例模式:
關(guān)于單例模式的實(shí)現(xiàn)有很多種,網(wǎng)上也分析了如今實(shí)現(xiàn)單利模式最好用枚舉,好處不外乎三點(diǎn):1.線程安全 2.不會(huì)因?yàn)樾蛄谢a(chǎn)生新實(shí)例 3.防止反射攻擊
1.線程安全
下面這段代碼就是聲明枚舉實(shí)例的通常做法,它可能還包含實(shí)例變量和實(shí)例方法,但是為了簡(jiǎn)單起見,我并沒有使用這些東西,僅僅需要小心的是如果你正在使用實(shí)例方法,那么你需要確保線程安全(如果它影響到其他對(duì)象的狀態(tài)的話)。默認(rèn)枚舉實(shí)例的創(chuàng)建是線程安全的,但是在枚舉中的其他任何方法由程序員自己負(fù)責(zé)。
3.防止反射攻擊
反射攻擊,我有自己試著反射攻擊了以下,不過報(bào)錯(cuò)了...看了下方的反編譯類源碼,明白了,因?yàn)閱卫惖男揎検莂bstract的,所以沒法實(shí)例化。(解決)
靜態(tài)內(nèi)部類:
關(guān)于單例模式的實(shí)現(xiàn)有很多種,網(wǎng)上也分析了如今實(shí)現(xiàn)單利模式最好用枚舉,好處不外乎三點(diǎn):1.線程安全 2.不會(huì)因?yàn)樾蛄谢a(chǎn)生新實(shí)例 3.防止反射攻擊
1.線程安全
下面這段代碼就是聲明枚舉實(shí)例的通常做法,它可能還包含實(shí)例變量和實(shí)例方法,但是為了簡(jiǎn)單起見,我并沒有使用這些東西,僅僅需要小心的是如果你正在使用實(shí)例方法,那么你需要確保線程安全(如果它影響到其他對(duì)象的狀態(tài)的話)。默認(rèn)枚舉實(shí)例的創(chuàng)建是線程安全的,但是在枚舉中的其他任何方法由程序員自己負(fù)責(zé)。
關(guān)于線程安全的保證,其實(shí)是通過類加載機(jī)制來保證的,我們看看INSTANCE的實(shí)例化時(shí)機(jī),是在static塊中,JVM加載類的過程顯然是線程安全的。
static {};
Code:
0: new #12; //class com/abin/lee/spring/util/Singleton$1
3: dup
4: ldc #14; //String INSTANCE
6: iconst_0
7: invokespecial #15; //Method com/abin/lee/spring/util/Singleton$1."<init>":(Ljava/lang/String;I)V
10: putstatic #19; //Field INSTANCE:Lcom/abin/lee/spring/util/Singleton;
13: iconst_1
14: anewarray #1; //class com/abin/lee/spring/util/Singleton
17: dup
18: iconst_0
19: getstatic #19; //Field INSTANCE:Lcom/abin/lee/spring/util/Singleton;
22: aastore
23: putstatic #21; //Field ENUM$VALUES:[Lcom/abin/lee/spring/util/Singleton;
26: return
線程安全,從反編譯后的類源碼中可以看出也是通過類加載機(jī)制保證的,應(yīng)該是這樣吧
2.不會(huì)因?yàn)樾蛄谢a(chǎn)生新實(shí)例枚舉自己處理序列化
傳統(tǒng)單例存在的另外一個(gè)問題是一旦你實(shí)現(xiàn)了序列化接口,那么它們不再保持單例了,因?yàn)閞eadObject()方法一直返回一個(gè)新的對(duì)象就像java的構(gòu)造方法一樣,你可以通過使用readResolve()方法來避免此事發(fā)生,看下面的例子://readResolve to prevent another instance of Singleton
private Object readResolve(){
return INSTANCE;
}
這樣甚至還可以更復(fù)雜,如果你的單例類維持了其他對(duì)象的狀態(tài)的話,因此你需要使他們成為transient的對(duì)象。但是枚舉單例,JVM對(duì)序列化有保證。優(yōu)點(diǎn):不僅能避免多線程同步問題,而且還能防止反序列化重新創(chuàng)建新的對(duì)象
反射攻擊,我有自己試著反射攻擊了以下,不過報(bào)錯(cuò)了...看了下方的反編譯類源碼,明白了,因?yàn)閱卫惖男揎検莂bstract的,所以沒法實(shí)例化。(解決)
靜態(tài)內(nèi)部類:
// Correct lazy initialization in Java
@ThreadSafe
class Foo {
private static class HelperHolder {
public static Helper helper = new Helper();
}
public static Helper getHelper() {
return HelperHolder.helper;
}
}
它利用了內(nèi)部靜態(tài)類只有在被引用的時(shí)候才會(huì)被加載的規(guī)律。
這樣一來,一旦內(nèi)部的HelperHolder被引用了,它就會(huì)首先被JVM加載,進(jìn)行該類的靜態(tài)域的初始化,從而使得Helper這一單例類被初始化。它之所以是線程安全的,也是托了JVM的福,因?yàn)?/span>JVM對(duì)于類的加載這一過程是線程安全的。