Singleton(單例)模式是指在程序運行期間, 某些類只實例化一次,建立一個全局惟一對象。所以,單例類只能有一個實例,且必須本身建立本身的這個惟一實例,並對外提供訪問該實例的方式。
單例模式主要是爲了不建立多個實例形成的資源浪費,以及多個實例屢次調用容易致使結果出現不一致等問題。例如,一個系統只能有一個窗口管理器或文件系統,一個程序只須要一份全局配置信息。java
根據加載的時機能夠分爲即時加載和延時加載兩種模式。數據庫
在單例類被加載時就建立單例的方式,稱爲即時加載單例(也稱餓漢式)。緩存
示例代碼以下:安全
public enum EnumSingleton { INSTANCE; public static EnumSingleton getInstance() { // 照顧開發者舊有習慣 return INSTANCE; } // 外部可調用EnumSingleton.INSTANCE.doSomething()或EnumSingleton.getInstance().doSomething() public void doSomething() { System.out.println("EnumSingleton: do something like accessing resources"); } }
此類單例具備如下優勢:工具
缺點則有:性能
示例代碼以下:線程
public class StaticFieldSingleton { public static final StaticFieldSingleton INSTANCE = new StaticFieldSingleton(); private StaticFieldSingleton() { // 私有化構造方法,防止外部實例化而破壞單例 if (INSTANCE != null) { // 防止反射攻擊 throw new UnsupportedOperationException(); } } // 外部可調用StaticFieldSingleton.INSTANCE.doSomething() public void doSomething() { System.out.println("StaticFieldSingleton: do something like accessing resources"); } }
示例代碼以下:日誌
public class StaticMethodSingleton { private static final StaticMethodSingleton INSTANCE = new StaticMethodSingleton(); // INSTANCE由private修飾 private StaticMethodSingleton() { if (INSTANCE != null) { // 防止反射攻擊 throw new UnsupportedOperationException(); } } public static StaticMethodSingleton getInstance() { return INSTANCE; } // 外部可調用StaticFieldSingleton.getInstance().doSomething() public void doSomething() { System.out.println("StaticMethodSingleton: do something like accessing resources"); } }
靜態工廠方法比靜態公有域單例更具靈活性:code
即時加載相對簡單,做爲主要推薦的單例模式。但在有些業務場景中,不但願單例被過早建立,而在真正使用的那刻才建立,即延時加載單例(也稱懶漢式)。此類場景有:對象
示例代碼以下:
public class StaticHolderSingleton { private static class SingletonHolder { private static final StaticHolderSingleton INSTANCE = new StaticHolderSingleton(); } private StaticHolderSingleton() {} public static StaticHolderSingleton getInstance() { return SingletonHolder.INSTANCE; } // 外部可調用StaticHolderSingleton.getInstance().doSomething() public void doSomething() { System.out.println("StaticHolderSingleton: do something like accessing resources"); } }
靜態內部類單例有如下特色:
示例代碼以下:
public class DCLSingleton { private static volatile DCLSingleton instance; // volatile禁止指令重排序,並保證內存可見性 private DCLSingleton() {} public static DCLSingleton getInstance() { if (instance == null) { // 此處判空旨在提升性能 synchronized (DCLSingleton.class) { if (instance == null) { instance = new DCLSingleton(); } } } return instance; } // 外部可調用DCLSingleton.getInstance().doSomething() public void doSomething() { System.out.println("DCLSingleton: do something like accessing resources"); } }
DCL單例比較複雜,並且用到synchronized和volatile,性能有所損失。
Java對象可經過new、克隆(clone)、反序列化(serialize)、反射(reflect)等方式建立。
經過私有化或不提供構造方法,可阻止外部經過new建立單例實例。其餘幾種建立方式則須要特別注意(枚舉單例不存在本節風險)。
java.lang.Obeject#clone()
方法不會調用構造方法,而是直接從內存中拷貝內存區域。所以,單例類不能實現Cloneable接口。
反射經過調用構造方法生成新的對象,可在構造方法中進行判斷,實例已建立時拋出異常,如StaticFieldSingleton所示。
普通Java類反序列化時會經過反射調用類的默認構造方法來初始化對象。若是單例類實現java.io.Serializable接口, 就能夠經過反序列化破壞單例。
所以,單例類儘可能不要實現序列化接口。如若必須,能夠重寫反序列化方法readResolve()
, 反序列化時直接返回相關單例對象:
public Object readResolve() { return instance; }
單例:在一個JVM中只容許一個實例存在。單例經常是帶有狀態的,能夠攜帶更豐富的信息,使用場景更加普遍。
靜態方法: 對於不須要維護任何狀態,僅提供全局訪問方法的類,可將其實現爲更簡單的靜態方法類(如各類Uitls工具類),它的速度更快。