單例模式有以下特點:
1、單例類只能有一個實例。
2、單例類必須自己創建自己的唯一實例。
3、單例類必須給所有其他對象提供這一實例。
單例模式確保某個類只有一個實例,而且自行實例化並向整個系統提供這個實例。在計算機系統中,執行緒池、快取、日誌對象、對話框、印表機、顯示卡的驅動程式對象常被設計成單例。這些套用都或多或少具有資源管理器的功能。每台計算機可以有若干個印表機,但只能有一個Printer Spooler,以避免兩個列印作業同時輸出到印表機中。每台計算機可以有若干通信連線埠,系統應當集中管理這些通信連線埠,以避免一個通信連線埠同時被兩個請求同時調用。總之,選擇單例模式就是為了避免不一致狀態,避免政出多頭。
首先看一個經典的單例實現。
[java]view plaincopyprint?
public class Singleton{
private static Singleton unique Instance=null;
private Singleton(){
//Exists only to defeat instantiation.
}
public static Singleton getInstance(){
if(unique Instance==null){
unique Instance=new Singleton();
}
return unique Instance;
}
//Other methods...
}
1.public class Singleton{
2.private static Singleton unique Instance=null;
3.4.private Singleton(){
5.//Exists only to defeat instantiation.
6.}
7.8.public static Singleton getInstance(){
9.if(unique Instance==null){
10.unique Instance=new Singleton();
11.}
12.return unique Instance;
13.}
14.//Other methods...
15.}
Singleton通過將構造方法限定為private避免了類在外部被實例化,在同一個虛擬機範圍內,Singleton的唯一實例只能通過getInstance()方法訪問。(事實上,通過Java反射機制是能夠實例化構造方法為private的類的,那基本上會使所有的Java單例實現失效。此問題在此處不做討論,姑且掩耳盜鈴地認為反射機制不存在。)
但是以上實現沒有考慮執行緒安全問題。所謂執行緒安全是指:如果你的代碼所在的進程中有多個執行緒在同時運行,而這些執行緒可能會同時運行這段代碼。如果每次運行結果和單執行緒運行的結果是一樣的,而且其他的變數的值也和預期的是一樣的,就是執行緒安全的。或者說:一個類或者程式所提供的接口對於執行緒來說是原子操作或者多個執行緒之間的切換不會導致該接口的執行結果存在二義性,也就是說我們不用考慮同步的問題。顯然以上實現並不滿足執行緒安全的要求,在並發環境下很可能出現多個Singleton實例。
[java]view plaincopyprint?
package jason.single;
public class TestStream{
Stringname=null;
public String getName(){
return name;
}
public void setName(Stringname){
this.name=name;
}
private Test Stream(){
}
private static TestStreamts1=null;
public static TestStream getTest(){
if(ts1==null){
ts1=new TestStream();
}
returnts 1;
}
public void printInfo(){
System.out.println("the name is"+name);
}
}
1.package jason.single;
2.3.public class TestStream{
4.Stringname=null;
5.6.public String getName(){
7.return name;
8.}
9.10.public void setName(Stringname){
11.this.name=name;
12.}
13.14.private Test Stream(){
15.}
16.17.private static TestStreamts1=null;
18.19.public static TestStream getTest(){
20.if(ts1==null){
21.ts1=new TestStream();
22.}
23.returnts 1;
24.}
25.26.public void printInfo(){
27.System.out.println("the name is"+name);
28.}
29.30.}
[java]view plaincopyprint?
package jason.single;
public class TMain{
public static void main(String[]args){
Test Streamts1=TestStream.getTest();
ts1.setName("jason");
TestStreamts2=TestStream.getTest();
ts2.setName("0539");
ts1.printInfo();
ts2.printInfo();
if(ts1==ts2){
System.out.println("創建的是同一個實例");
}else{
System.out.println("創建的不是同一個實例");
}
}
}
1.package jason.single;
2.3.public class TMain{
4.public static void main(String[]args){
5.Test Streamts1=TestStream.getTest();
6.ts1.setName("jason");
7.TestStreamts2=TestStream.getTest();
8.ts2.setName("0539");
9.10.ts1.printInfo();
11.ts2.printInfo();
12.13.if(ts1==ts2){
14.System.out.println("創建的是同一個實例");
15.}else{
16.System.out.println("創建的不是同一個實例");
17.}
18.}
19.}
運行結果:
結論:由結果可以得知單例模式為一個面向對象的應用程式提供了對象惟一的訪問點,不管它實現何種功能,整個應用程式都會同享一個實例對象。
1.餓漢式單例類
[java]view plaincopyprint?
//餓漢式單例類.在類初始化時,已經自行實例化
public class Singleton1{
//私有的默認構造子
private Singleton1(){}
//已經自行實例化
private static final Singleton1 single=new Singleton1();
//靜態工廠方法
public static Singleton1 getInstance(){
return single;
}
}
1.//餓漢式單例類.在類初始化時,已經自行實例化
2.public class Singleton1{
3.//私有的默認構造子
4.private Singleton1(){}
5.//已經自行實例化
6.private static final Singleton1 single=new Singleton1();
7.//靜態工廠方法
8.public static Singleton1 getInstance(){
9.return single;
10.}
11.}
2.懶漢式單例類
[java]view plaincopyprint?
//懶漢式單例類.在第一次調用的時候實例化
public class Singleton2{
//私有的默認構造子
private Singleton2(){}
//注意,這裡沒有final
private static Singleton2 single=null;
//靜態工廠方法
public synchronized static Singleton2 getInstance(){
if(single==null){
single=new Singleton2();
}
return single;
}
}
1.//懶漢式單例類.在第一次調用的時候實例化
2.public class Singleton2{
3.//私有的默認構造子
4.private Singleton2(){}
5.//注意,這裡沒有final
6.private static Singleton2 single=null;
7.//靜態工廠方法
8.public synchronized static Singleton2 getInstance(){
9.if(single==null){
10.single=new Singleton2();
11.}
12.return single;
13.}
14.}
[java]view plaincopyprint?
import java.util.HashMap;
import java.util.Map;
//登記式單例類.
//類似Spring裡面的方法,將類名註冊,下次從裡面直接獲取。
public class Singleton3{
private static Mapmap=new HashMap();
static{
Singleton3 single=new Singleton3();
map.put(single.getClass().getName(),single);
}
//保護的默認構造子
protected Singleton3(){}
//靜態工廠方法,返還此類惟一的實例
public static Singleton3 getInstance(Stringname){
if(name==null){
name=Singleton3.class.getName();
System.out.println("name==null"+"--->name="+name);
}
if(map.get(name)==null){
try{
map.put(name,(Singleton3)Class.forName(name).newInstance());
}catch(InstantiationExceptione){
e.printStackTrace();
}catch(IllegalAccessExceptione){
e.printStackTrace();
}catch(ClassNotFoundExceptione){
e.printStackTrace();
}
}
returnmap.get(name);
}
//一個示意性的商業方法
public String about(){
return"Hello,IamRegSingleton.";
}
public static void main(String[]args){
Singleton3 single3=Singleton3.getInstance(null);
System.out.println(single3.about());
}
}
1.import java.util.HashMap;
2.import java.util.Map;
3.//登記式單例類.
4.//類似Spring裡面的方法,將類名註冊,下次從裡面直接獲取。
5.public class Singleton3{
6.private static Mapmap=new HashMap();
7.static{
8.Singleton3 single=new Singleton3();
9.map.put(single.getClass().getName(),single);
10.}
11.//保護的默認構造子
12.protected Singleton3(){}
13.//靜態工廠方法,返還此類惟一的實例
14.public static Singleton3 getInstance(Stringname){
15.if(name==null){
16.name=Singleton3.class.getName();
17.System.out.println("name==null"+"--->name="+name);
18.}
19.if(map.get(name)==null){
20.try{
21.map.put(name,(Singleton3)Class.forName(name).newInstance());
22.}catch(InstantiationExceptione){
23.e.printStackTrace();
24.}catch(IllegalAccessExceptione){
25.e.printStackTrace();
26.}catch(ClassNotFoundExceptione){
27.e.printStackTrace();
28.}
29.}
30.returnmap.get(name);
31.}
32.//一個示意性的商業方法
33.public String about(){
34.return"Hello,IamRegSingleton.";
35.}
36.public static void main(String[]args){
37.Singleton3 single3=Singleton3.getInstance(null);
38.System.out.println(single3.about());
39.}
40.}