單例模式的特點:在同一時期,某個類的對象一定最多隻有1個!也許會嘗試多次的擷取對象,但是,擷取到的一定是同一個對象!
假設項目中有King類:
public class King {
}
很顯然,目前它并不是單例的,因為,可以:
King k1 = new King();
King k2 = new King();
King k3 = new King();
以上代碼就建立了3個King類型的對象!如果要實作單例,首先,就必須限制構造方法的通路,例如:
public class King {
private King() {
}
}
每個類中都可以有若幹個構造方法,如果某個類沒有顯式的聲明任何構造方法,編譯器就會自動添加1個公有的、無參數的構造方法!如果類中已經聲明任何構造方法,則編譯器不會自動添加構造方法!
由于将構造方法聲明為私有的,則原有的King k1 = new King();這類代碼就不能用于建立對象了!
限制構造方法的通路,其目的是“不允許随意建立對象”,并不是“不允許建立對象”,在King類的内部,還是可以建立對象的,可以添加方法,傳回内部建立的對象:
public class King {
private King king = new King();
private King() {
}
public King getInstance() {
return king;
}
}
是以,當需要King類型的對象時,可以通過getInstance()方法來擷取!
但是,以上代碼是不可行的!因為,如果要調用getInstance()方法,必須先擷取King的對象,而擷取King對象的唯一方式就是調用getInstance()方法!為了解決這個問題,必須在getInstance()方法的聲明之前添加static修飾符,最終,就可以通過類名.方法名()的文法格式來調用方法了!同時,由于“被static修飾的成員,不可以通路其它未被static修飾的成員”,是以,全局屬性king也必須被static修飾:
public class King {
private static King king = new King();
private King() {
}
public static King getInstance() {
return king;
}
}
至此,基本的單例模式的代碼就設計完成了!
以上代碼是“餓漢式”的單例模式,另外,還有“懶漢式”的單例模式!
基本的懶漢式單例模式的代碼是:
public class King {
private static King king = null;
private King() {
}
public static King getInstance() {
if (king == null) {
king = new King();
}
return king;
}
}
注意:以上代碼是多線程不安全的!
在開發領域中,隻要資料的産生、變化不是開發人員預期的,就稱之為“不安全”,也就是“資料安全問題”。
為了保障線程安全,應該為以上建立對象的代碼片斷“加鎖”,例如:
public class King {
private static King king = null;
private King() {
}
public static King getInstance() {
synchronized ("hello") {
if (king == null) {
king = new King();
}
}
return king;
}
}
當然,無論是哪個線程在什麼時候執行以上代碼,都必須先“鎖住”代碼片斷後才能開始執行,是沒有必要的,“鎖”的性能消耗是浪費的,是以,可以進一步調整為:
public class King {
private static King king = null;
private King() {
}
public static King getInstance() {
if (king == null) { // 判斷有沒有必要鎖定接下來的代碼
synchronized ("java") {
if (king == null) { // 判斷有沒有必要建立對象
king = new King();
}
}
}
return king;
}
}