設計模式-單例(Singleton)

2018-1-12 by Atlas


  • 設計思想

單例模式確保某一個類只有一個實例,而且自行實例化並向整個系統提供這個實例單例模式。單例模式只應在有真正的“單一實例”的需求時纔可使用。

  • 應用場景

(1)如果有1個以上的對象實例時,由於對象實例彼此之間的影響,可能會發展成出乎意料的BUG。
(2)需要控制類的對象實例的數量,以降低資源的使用時。

  • UML 類圖

Singleton UML

  • UML中加“-”表示私有的(private);
  • UML中加“+”表示公有的(public);
  • UML中加“_”表示靜態的(static)。

Singleton模式基本構成:

  • 靜態的私有的類成員變量singleton,私有的表示只有成員所生存的對象可以訪問,靜態的表示類加載準備階段分配初始值、解析階段字符引用轉化爲直接引用;
  • 私有的類構造方法Singleton,私有的構造方法表示禁止非Singleton類調用實例構造器創建對象實例;
  • 靜態的公有的類方法getInstance,靜態的類方法表示類加載解析階段就從字符引用轉化爲直接引用,即內存中方法的實際地址,可以通過類.方法直接調用。
  • 標準示例
public class Singleton {
    private static Singleton singleton = new Singleton();
    private Singleton() {
        System.out.println("已產生對象實例。");
    }
    public static Singleton getInstance() {
        return singleton;
    }
}
public static void main(String[] args) {
    Singleton s1 = Singleton.getInstance();
    Singleton s2 = Singleton.getInstance();
    System.out.println(s1 == s2);
}
  • 拓展示例

延遲對象實例化

  • 優點:
    類加載時不進行目標對象的實例化操作,提高了類加載的速度,實際訪問需要時才進行目標對象的實例化。
  • 缺點:
    相比類加載時就進行目標對象實例化,延遲實例化可能導致多線程併發時產生線程安全問題,需要同步訪問入口方法以達到線程安全的目的,理論上降低了多線程併發訪問的效率。
public class Singleton {
    private static Singleton singleton;
    private Singleton() {
            System.out.println("已產生對象實例。");
    }
    public static synchronized Singleton getInstance() {
            if(singleton == null){
                singleton = new Singleton();
            }
            return singleton;
    }
}
public static void main(String[] args) {
    Singleton s1 = Singleton.getInstance();
    Singleton s2 = Singleton.getInstance();
    System.out.println(s1 == s2);
}

那麼問題來了,有沒有一種兩全其美的方式呢,答案是有的。
靜態內部類實例化

  • 優點:
    (1)外部類加載時無須進行目標對象的實例化操作,提高了類加載的速度,實際訪問需要時加載內部類並進行目標對象的實例化。
    (2)靜態內部類只會進行一次內部類的類變量的初始化,不會產生線程安全問題,無須同步,不會降低多線程併發訪問效率。
public class Singleton {
    private Singleton() {
        System.out.println("已產生對象實例。");
    }
    private static class SingletonHolder {
        private static Singleton singleton = new Singleton();
    }
    public static Singleton getInstance() {
        return SingletonHolder.singleton;
    }
}
  • 案例鑑賞

java.lang.Runtime#getRuntime()

public class Runtime {
    private static Runtime currentRuntime = new Runtime();
    /**
     * Returns the runtime object associated with the current Java application.
     * Most of the methods of class <code>Runtime</code> are instance
     * methods and must be invoked with respect to the current runtime object.
     *
     * @return  the <code>Runtime</code> object associated with the current
     *          Java application.
     */
    public static Runtime getRuntime() {
        return currentRuntime;
    }
    /** Don't let anyone else instantiate this class */
    private Runtime() {}
    // 忽略其他
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章