單例模式的討論珠玉在前,我就不過多敘述基礎(chǔ)的內(nèi)容。感興趣的朋友可以閱讀參考資料1。
假設(shè)現(xiàn)在有一種變形的餓漢式單例,單例的賦值是在類的構(gòu)造函數(shù)里面進行的。樣例代碼如下:
public class SingletonSample{
private static SingletonSample mInstance;
private SingletonSample(String arg){
mInstance = this;
}
public static SingletonSample getInstance(){
return mInstance;
}
public void doSomething(){
//做一些事情
...
}
}
那么上文的getInstance函數(shù)是不能保證返回值非空的。那在程序中就有可能出現(xiàn)空指針,進而導致崩潰。
限制單例的賦值不能變換位置的話,現(xiàn)在有兩種解決方案擺在我們面前:
- getInstance方法不保證非空,在外部任一調(diào)用getInstance函數(shù)的地方先進行非空判定再執(zhí)行相關(guān)方法。
- getInstance方法保證非空。
顯然方案一是不需要更多思考的,直接就是空指針情形的常見解決方案,判空,非空則執(zhí)行邏輯,否則不執(zhí)行。但這種解決方案我覺得不好,既要修改已有的很多個調(diào)用處,即多個文件,還要保證之后調(diào)用的地方都自覺加上非空判定。
所以我選擇方案二。但是根據(jù)限制條件“單例的賦值不能變換位置”,自然是不能把當前這個單例轉(zhuǎn)為標準的餓漢式或懶漢式。既然不能隨意改動到mInstance的值,那有沒有別的方法達成getInstance方法保證非空的目的呢?
哈哈哈哈,當然有啦,mInstance為null的時候返回一個空的占位符實例不就行了嗎。
修改后的樣例代碼如下:
public class SingletonSample{
private static SingletonSample mInstance;
private static final SingletonSample PLACEHOLDER = new SingletonSample() ;
private SingletonSample(){
//空的構(gòu)造函數(shù),純粹為了占位符而生
}
private SingletonSample(String arg){
mInstance = this;
}
public static SingletonSample getInstance(){
if (mInstance == null) {
return PLACEHOLDER;
}
return mInstance;
}
public void doSomething(){
if(!isValidInstance()){
return;
}
//做一些事情
...
}
private boolean isValidInstance(){
return this != PLACEHOLDER;
}
}
這其實也是空指針情形的一種常見解決方案,空的時候返回一個默認值/占位符,不空的時候返回實際值。
可以看到還加了個isValidInstance的判斷函數(shù),原因是實際邏輯操作的時候,可能占位符/默認值并不能執(zhí)行,所以要在所有的對外方法中添加實例檢查。
這樣修改后,其實還是不可避免地要進行多處修改,但這次的多處修改都是在當前單例類里面的,不會涉及外部類;同時也是要保證之后當前單例類里新增的對外實例方法,都要進行實例驗證才能進行邏輯操作,但是這仍舊是當前類里的修改,不涉及外部類。
這個解決方案在我看來,雖然還有類似的限制(要改動多處,對之后的邏輯有要求),但是還是比第一種解決方案要優(yōu)秀,因為外部調(diào)用處有可能不是同一個開發(fā)者書寫的邏輯;但把限制都約束在了同一個類里,既避免了空指針發(fā)散,也在很多情況下是同一個開發(fā)者維護的邏輯,更能避免后續(xù)Bug的產(chǎn)生。
看回PLACEHOLDER這個靜態(tài)變量,它會不會有可能為null呢?
不會。
這個變量的賦值是在類初始化的時候,這個值為null的話,只有可能是相關(guān)的ClassLoader都被銷毀了,否則只要這個類有加載到ClassLoader中初始化,這個變量就都不會為null。詳細參見參考資料2。
參考資料