2018-1-12 by Atlas
- 設計思想
單例模式確保某一個類只有一個實例,而且自行實例化並向整個系統提供這個實例單例模式。單例模式只應在有真正的“單一實例”的需求時纔可使用。
- 應用場景
(1)如果有1個以上的對象實例時,由於對象實例彼此之間的影響,可能會發展成出乎意料的BUG。
(2)需要控制類的對象實例的數量,以降低資源的使用時。
- 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() {}
// 忽略其他
}