單例模式Signleton的實現及破壞

本篇博客轉載自: 點擊打開鏈接 http://www.cnblogs.com/fernandolee24/p/5366720.html

一、單例的實現
Singleton從表面上看是希望並限制該類的實例只能有一個,(比如Runtime類),其構造方式通常是一個private構造函數、static的該類實例、以及返回該實例的getInstance方法。
1、餓漢式(Eager Signleton)
該實例會在類加載的時候自動創建,不管是否使用。
public class DemoSignleton{
    private DemoSignleton(){  }
    private static final DemoSignleton INSTANCE = new DemoSignleton();

    public static DemoSignleton getInstance(){
        return INSTANCE;
    }
}

2、懶漢式(Lazy Signleton)
類實例會在使用的時候才創建。但是由於加了synchronized 所以代價高昂,實際只有在初始,該實例未被創建的時候纔有必要加鎖,當實例一旦被創建過了就沒有必要加鎖控制了!
public class LazySignleton {
    private volatile static LazySignleton INSTANCE  = null;
    private LazySignleton(){ }

    pbulic static synchronized LazySignleton getInstance(){
        if(INSTANCE == null)
            INSTANCE = new LazySignleton();
         return INSTANCE;
    }
}
3、懶漢式的改進
當然既然懶漢式有這個缺點,我們可以單獨對其處理,如果實例已經創建過了,就不需要進行加鎖了,如下是改良後的實例方式:
public static LazySignleton getInstance(){
    if(INSTANCE == null){
        synchronized(LazySignleton.class){
                if(INSTANCE == null){
                    INSTANCE = new LazySignleton();
                }
        }
    }
    return INSTANCE;
}

4、靜態內部類實現單例(inner class Signleton)(該方法比較常用)
這種方法有效的解決了併發問題,並延遲了實例化,
public class InnerClassSignleton {
    
    public static class SignletonHolder {
        private static final InnerClassSignleton INSTANCE = new InnerClassSignleton();
    } 

    private InnerClassSignleton(){ }

    public static final InnerClassSignleton getInstance(){
        return SignletonHolder.INSTANCE;
    }
}
二、單例的破壞
有時我們需要對單例進行破壞,下面是幾種破壞的方法;
1、第一種比較容易想到的方式就是clone,Java中類通過實現Clonable接口並覆寫clone方法就可以完成一個其對象的拷貝。而當Singleton類爲Clonable時也自然無法避免可利用這種方式被重新創建一份實例。

2、第二種破壞方式就是利用序列化與反序列化,當Singleton類實現了Serializable接口就代表它是可以被序列化的,該實例會被保存在文件中,需要時從該文件中讀取並反序列化成 對象。可就是在反序列化這一過程中不知不覺留下了可趁之機,因爲默認的反序列化過程是繞開構造函數直接使用字節生成一個新的對象。

3、前兩種破壞方式說到底都是通過避免與私有構造函數正面衝突的方式另闢蹊徑來實現的,而這種方式就顯得藝高人膽大,既然你是私有的不允許外界直接調用,那麼我就利用反射機制強行逼你就範:公開其訪問權限。

4、Java中一個類並不是單純依靠其全包類名來標識的,而是全包類名加上加載它的類加載器共同確定的。因此,只要是用不同的類加載器加載的Singleton類並不認爲是相同的。

三、一個真正安全的單例
1、 Safe Signleton
public class SafeSingleton implements Serializable, Cloneable {
 
    private static final long serialVersionUID = -4147288492005226212L;
 
    private static SafeSingleton INSTANCE = new SafeSingleton();
 
    private SafeSingleton() {
        if (INSTANCE != null) {
            throw new IllegalStateException("Singleton instance Already created.");
        }
    }
 
    public static SafeSingleton getInstance() {
        return INSTANCE;
    }
 
    private Object readResolve() throws ObjectStreamException {
        return INSTANCE;
    }
 
    public Object clone() throws CloneNotSupportedException {
        throw new CloneNotSupportedException("Singleton can't be cloned");
    }
}

在原有Singleton的基礎上完善若干方法即可實現一個安全的更爲純正的Singleton。注意到當實例已經存在時試圖通過調用私有構造函數會直接報錯從而抵禦了反射機制的入侵; 讓調用clone方法直接報錯避免了實例被克隆;覆寫readReslove方法直接返回現有的實例本身可以防止反序列化過程中生成新的實例。而對於不同類加載器導致的單例模式破壞暫未找到切實可行的應對方案,請大牛提供高見。


2、Enum Signleton

public enum EnumSingleton{
    INSTANCE;
 
    private EnumSingleton(){
    }
}

採用枚舉的方式實現Singleton非常簡易,而且可直接通過EnumSingleton.INSTANCE獲取該實例。Java中所有定義爲enum的類內部都繼承了Enum類,而Enum具備的特性包括類加載是靜態的來保證線程安全,而且其中的clone方法是final的且直接拋出CloneNotSupportedException異常因而不允許拷貝,同時與生俱來的序列化機制也是直接由JVM掌控的並不會創建出新的實例,此外Enum不能被顯式實例化反射破壞也不起作用。當然它也不是沒有缺點,比如由於已經隱式繼承Enum所以無法再繼承其他類了(Java的單繼承模式限制),而且相信大多數人並不樂意僅僅爲了實現一個純正的Singleton就將習慣的class修改爲enum。


受益很多,再次感謝原創作者: http://www.cnblogs.com/fernandolee24/p/5366720.html

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章