現在大多數Java軟件工程師面試都會問到這個問題:什么是單例模式(Singleton),能否寫出單例模式的示例代碼?單例模式是Gof中23個模式中最簡單最容易入門的模式,學習它我們能更理性的感知模式的意義.
[形成]
Singleton Pattern 為什么會出現?在我們軟件開發和架構中,經常遇到這樣的情形:我們需要一個類只能且僅能產生一個實例..比如表示一臺計算機的類,表示系統設定的類或者是表示窗口的類,還有為了節約資源,只讓產生一個實例..
如何構造這種情形?Singleton模式給我一個方案:
[代碼示例]
程序列表
名稱
|
說明
|
Singleton
|
只有一個對象實例的類
|
Main
|
測試用的類
|
[UML圖]

[示例代碼和類的詮釋]
1 package singleton;
2
3 public class Singleton {
4 private static Singleton singleton = new Singleton();
5
6 private Singleton() {
7 System.out.println("Create instance...");
8 }
9
10 public static Singleton getInstance() {
11 return singleton;
12 }
13 }
14
Singleton Class:
1.該類只能產生一個對象實例
2.把該類的的singleton屬性設定為static再以Singleton;類的對象實例進行初始化,這個初始化的過程僅加載Sington類的時候調用一次.(Line4)
3.把Singleton 類的構造函數限定為private,目的是為了防止從非Singleton類(其他類)調用構造函數來產生實例,如果通過new方式來產生Singleton實例,會出現編譯錯誤.這樣做是為了保險己見.(Line6)
4.要得到Singleton實例的唯一方法就是調用類靜態方法getInstance().這個名字可以隨便取,只要方便理解就行.(Line 10)
1 package singleton;
2
3 public class Main {
4
5 public static void main(String[] args) {
6 System.out.println("Start");
7 Singleton obj1 = Singleton.getInstance();
8 Singleton obj2 = Singleton.getInstance();
9 if(obj1 == obj2){
10 System.out.println("obj1和obj2是同一個對象實例");
11 }else{
12 System.out.println("obj1和obj2不是同一個對象實例");
13 }
14 System.out.println("End");
15 }
16 }
17
Main Class
1.該類是測試程序.
2.程序通過getInstance()方式產生兩個obj1和obj2實例.(Line 7,Line 8)
3.通過ojb1= =ojb2表達式來確定兩個對象是否相同,判定是否產生了Singleton的第二個示例.(Line9-12)
執行結果含義:
1. 的確如此,obj1和obj2是Singleton類的同一個且唯一的對象實例.
2.當程序執行后,第一次調用getInstance的時候會初始化Singleton類,同時也會初始化static字段,也同時產生產生了一個唯一對象實例.
[拓展思考]
如下的另一一個單例模式的程序有什么隱患?
1 package singleton;
2
3 public class Singleton2 {
4
5 private static Singleton2 singleton = null;
6
7 private Singleton2(){
8 System.out.println("已產生對象實例");
9 }
10 public static Singleton2 getInstance(){
11 if(singleton == null){
12 singleton = new Singleton2();
13 }
14 return singleton;
15 }
16
17 }
18
[解答]
當多線程同時調用Singleton2.getInstance()方法時,可能會產生多個對象實例,例如

public class Main extends Thread
{


public static void main(String[] args)
{
System.out.println("Starts.");
new Main("A").start();
new Main("B").start();
new Main("C").start();
System.out.println("End.");
}

public void run()
{
Singleton2 obj = Singleton2.getInstance();
System.out.println(getName()+": obj ="+obj);
}

public Main(String name)
{
super(name);
}
}


public class Singleton2
{
private static Singleton2 singleton2 = null;

private Singleton2()
{
System.out.println("已產生對象實例");
solwDown();
}


public static Singleton2 getInstance()
{

if(singleton2 == null)
{
singleton2 = new Singleton2();
}
return singleton2;
}

private void solwDown()
{

try
{
Thread.sleep(1000);

}catch(InterruptedException e)
{
e.printStackTrace();
}
}
}

執行結果:
Start.
End.
已產生對象實例.
已產生對象實例.
已產生對象實例.
B: obj = Singleton2#2a9348
C: obj = Singleton2#b91134
A: obj = Singleton2#e343l12
(#替換為@)
之所以會知道這種情況是因為if(singleton = = null){ singleton = new Singleton2(); }判斷不夠嚴謹的導致。
利用: singleton == null 判斷為空后去執行new Singleton2()之前,可能會有其他線程來搶先判斷表達式singleton == null,從而又執行一遍創建實例的操作。
解決辦法:
給getInstance()方法添加Synchronized修飾符,即可修改成線程安全嚴謹的單例模式。
public class Singleton2 {
private static Singleton2 singleton = null;
private Singleton2() {
System.out.println("已產生對象實例");
solwDown();
}
public static synchronized Singleton2 getInstance() {
if (singleton == null) {
singleton = new Singleton2();
}
return singleton;
}
private void solwDown() {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}