Singleton單例模式是最簡單的設計模式,它的主要做用是保證在程序運行生命週期中,使用了單例模式的類只能有一個實例對象存在。單例模式實現了相似C語言中全局變量的功能,單例模式經常使用於註冊/查找的服務。 java
單例模式的UML圖以下: 設計模式
單例模式有兩種實現方式:飽漢模式和餓漢模式,以下: 安全
1.飽漢單例模式例子代碼: 多線程
public class Singleton1{ //飽漢模式,聲明時就建立實例對象 public static final Singleton1 instance = new Singleton1(); //單類模式的構造方法必須爲private,以免經過構造方法建立對象實例, //而且必須顯示聲明構造方法,以防止使用默認構造方法 private Singleton1(){} //單類模式必須對外提供獲取實例對象的方法 public static Singleton1 geInstance(){ return instance; } }
2.餓漢單例模式即延遲初始化單例方式,例子代碼: spa
public class Singleton2{ //餓漢模式,聲明時不建立實例對象 public static Singleton2 instance; //單類模式的構造方法必須爲private,以免經過構造方法建立對象實例, //而且必須顯示聲明構造方法,以防止使用默認構造方法 private Singleton2(){} //單類模式必須對外提供獲取實例對象的方法,延遲初始化的單類模式必須使用synchronized同步關鍵字,不然多線程狀況下很容易產生多個實例對象 public static synchronized Singleton2 geInstance(){ //延遲初始化,只有當第一次使用時才建立對象實例 if(instance == null){ instance = new Singleton2(); } return instance; } }
通常認爲飽漢模式要比餓漢模式更加安全。 線程
上面兩種Singleton單例設計模式的實現方式都隱藏有以下的問題: 設計
(1).雖然構造方式的訪問修飾符爲private,即除了自身之外其餘任何類都沒法調用,可是經過反射機制的setAccessiable(true)方法能夠訪問私有方法和屬性。所以Singleton單例模式必須考慮這種例外狀況。 code
(2).對象序列化以後再反序列化時會生成新的對象,所以當Singleton單例模式類實現序列化接口時,必須顯式聲明全部的字段爲tranisent,而且提供以下的readResolve方法來防止經過序列化破壞單態模式: 對象
private Object readResolve(){ return INSTANCE; }
3.使用Lazy initialization holder class模式實現單態: 繼承
public class Singleton3 { /** * 類級的內部類,也就是靜態的成員式內部類,該內部類的實例與外部類的實例 * 沒有綁定關係,並且只有被調用到纔會裝載,從而實現了延遲加載 */ private static class SingletonHolder{ //靜態初始化器,由JVM來保證線程安全 private static Singleton3 instance = new Singleton3(); } //私有化構造方法 private Singleton3(){ } public static Singleton3 getInstance(){ return SingletonHolder.instance; } }當getInstance方法第一次被調用的時候,它第一次讀取SingletonHolder.instance,致使SingletonHolder類獲得初始化;而這個類在裝載並被初始化的時候,會初始化它的靜態域,從而建立Singleton的實例,因爲是靜態的域,所以只會被虛擬機在裝載類的時候初始化一次,並由虛擬機來保證它的線程安全性。
4.在JDK1.5以後引入了Enum枚舉,所以在JDK1.5以後Singleton單例模式又有了第三種實現方式,也是最好的實現方式,例子以下:
public enum Singleton4{ INSTANCE{ public void doSomething(){ ... } }; public abstract void doSomething(); }Singleton單例模式中只有一個INSTANCE枚舉元素,枚舉能夠保證真個程序生命週期中只有一個實例對象存在,同時還避免了常規Singleton單例模式private構造方法被反射調用和序列化問題(枚舉提供了序列化保證機制,確保屢次序列化和反序列化不會建立多個實例對象)。
注意:java中除了構造方法能夠建立對象實例之外,還能夠經過克隆方法(clone()是Object中的protected方法)來建立對象,若單例對象直接繼承自Object對象,則若是沒有提供具體clone方法實現,則當調用克隆方法建立對象時,會拋出運行時的異常CloneNotSupportedException。
若單例類繼承了實現克隆方法的類,則在單例類中必須覆蓋父類的克隆方法,顯式拋出異常CloneNotSupportedException。
另外,實現了單例模式的類不能再有派生子類,由於構造方式是私有的,子類沒法調用父類構造方法,所以達到了Final的效果。
JDK的中單例模式的應用:
java.lang.Runtime