Java 單例模式實現方式及其優缺點——單例模式還可以這樣實現

單例模式三種初始化方法及其優缺點

一、常見單例模式實現方式

  單例模式:顧名思義即確保一個類只有一個實例,而且自行實例化(私有構造方法)並向整個系統提供這個實例。
  常見的單例模式有三種模式:懶漢式單例、餓漢式單例、靜態內部類單例(也叫登記式單例、holder單例)。

1.餓漢式單例

  實現方式:

public class Singleton {
    private static final Singleton SINGLETON = new Singleton();
    private Singleton{}; // 該類不能被實例化
    public static Singleton getInstance() {
        return SINGLETON;
    }
}

  優點:沒有加鎖,執行效率會提高。
  缺點:類加載時就初始化,浪費內存。

2.懶漢式單例(也叫飽漢式單例)

  實現方式一同步方法:

public class Singleton {
    private static Singleton singleton;
    private Singleton{}; // 該類不能被實例化
    public synchronized static Singleton getInstance() {
        if (singleton == null) {
             singleton = new Singleton();
        }
        return singleton;
    }
}

  實現方式二同步代碼塊(雙重檢查鎖定方式,比方式一好):

public class Singleton {
    private static Singleton singleton;
    private Singleton{}; // 該類不能被實例化
    public static Singleton getInstance() {
        if (singleton == null) {
            synchronized(Singleton.class) {
                if(singleton == null) {
                    singleton = new Singleton();
                }
            }
        }
        return singleton;
    }
}

  優點:第一次調用才初始化,避免內存浪費。
  缺點:必須加鎖synchronized 才能保證單例,即需要考慮線程同步,否則多個線程同時調用getInstance方法,有可能回出現實例化多次,但加鎖會在一定程度上影響效率。

3.靜態內部類單例

  實現方式:

public class Singleton {
    private static Singleton singleton;
    private Singleton{}; // 該類不能被實例化
    public static Singleton getInstance() {
        return Holeder.SINGLETON;
    }
    private static class Holder {
        private final static Singleton SINGLETON = new Singleton();
    }
}

  優點:1)內部類只有在外部類被調用才加載,產生SINGLETON實例;2)不用加鎖。
  缺點:代碼稍微複雜。

4.避免反射入侵方法

  Java反射機制是指在運行狀態中,對於任意一個類都能夠知道這個類所有的屬性和方法;並且對於任意一個對象,都能夠調用它的任意一個方法。
  單例模式在常規情況下,只能通過 getInstance() 創建實例;但是通過反射也可以直接調用private方法創建實例,即可以多次調用私有構造方法,產生反射入侵的問題。
  避免反射入侵方法思路是避免單例類的構造方法被多次調用,可以通過改在私有構造方法和getInstance方法實現,下面的實現以內部靜態類模式爲例:

public class Singleton {
    private static boolean sFlag = true;
    private static Singleton singleton;
    private Singleton{
        if (sFlag) {
            sFlag = fase;
        } else { // 防止被實例化多次
            throw new RuntimeException("單例模式被攻擊了");
        }
    }; // 該類不能被實例化
    public static Singleton getInstance() {
        return Holeder.SINGLETON;
    }
    private static class Holder {
        private final static Singleton SINGLETON = new Singleton();
    }
}

二、高效單例模式實現——枚舉類型單例模式

  上面介紹的三種單例實現方式,是比較常見的方式,下面介紹一種更簡單而且高效的單例模式——枚舉類型單例模式。
實現方式一:構造方法中實例化對象

// 枚舉單例模式定義
public enum Singleton {
    INSTANCE;
    public Singleton getInstance() {
        return INSTANCE;
    }

    public void method1() {

    }
}

// 枚舉單例使用
Singleton.INSTANCE.method1();
// 枚舉單例模式定義
public class EnumSingleton {
    private EnumSingleton(){}
    
    public static EnumSingleton getInstance(){
        return Singleton.INSTANCE.getInstance();
    }

    private enum Singleton{
        INSTANCE;
        private EnumSingleton singleton;
        //JVM會保證此方法絕對只調用一次
        Singleton(){
            singleton = new EnumSingleton();
        }
        public EnumSingleton getInstance(){
            return singleton;
        }
    }
}

// 枚舉單例使用
EnumSingleton.getInstance().method1();

實現方式二:枚舉常量的值即爲對象實例

public class EnumSingleton {
    private EnumSingleton(){}
    public static EnumSingleton getInstance(){
        return Singleton.INSTANCE.getInstance();
    }

    private enum Singleton{
        // 枚舉常量的值即爲對象實例
        INSTANCE(new EnumSingleton());
        private EnumSingleton singleton;
        //JVM會保證此方法絕對只調用一次
        Singleton(EnumSingleton singleton){
            this.singleton = singleton;
        }
        public EnumSingleton getInstance(){
            return singleton;
        }
    }
}

// 枚舉單例使用
EnumSingleton.getInstance().method1();

優點:

  • 枚舉單例有序列化和線程安全的保證,而且JVM會保證枚舉類構造方法絕對只調用一次,所以保證了對象實例的唯一性;
  • 枚舉ENUM類型,不允許反射調用,天生的避免了反射入侵(驚不驚喜,神不神奇!!!)。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章