單例模式的討論珠玉在前,我就不過多敘述基礎(chǔ)的內(nèi)容敲茄。感興趣的朋友可以閱讀參考資料1位谋。
假設(shè)現(xiàn)在有一種變形的餓漢式單例山析,單例的賦值是在類的構(gòu)造函數(shù)里面進(jìn)行的堰燎。樣例代碼如下:
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)空指針笋轨,進(jìn)而導(dǎo)致崩潰秆剪。
限制單例的賦值不能變換位置的話,現(xiàn)在有兩種解決方案擺在我們面前:
- getInstance方法不保證非空爵政,在外部任一調(diào)用getInstance函數(shù)的地方先進(jìn)行非空判定再執(zhí)行相關(guān)方法仅讽。
- getInstance方法保證非空。
顯然方案一是不需要更多思考的钾挟,直接就是空指針情形的常見解決方案洁灵,判空,非空則執(zhí)行邏輯掺出,否則不執(zhí)行徽千。但這種解決方案我覺得不好,既要修改已有的很多個(gè)調(diào)用處汤锨,即多個(gè)文件双抽,還要保證之后調(diào)用的地方都自覺加上非空判定。
所以我選擇方案二闲礼。但是根據(jù)限制條件“單例的賦值不能變換位置”牍汹,自然是不能把當(dāng)前這個(gè)單例轉(zhuǎn)為標(biāo)準(zhǔn)的餓漢式或懶漢式铐维。既然不能隨意改動(dòng)到mInstance的值,那有沒有別的方法達(dá)成getInstance方法保證非空的目的呢慎菲?
哈哈哈哈嫁蛇,當(dāng)然有啦,mInstance為null的時(shí)候返回一個(gè)空的占位符實(shí)例不就行了嗎露该。
修改后的樣例代碼如下:
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;
}
}
這其實(shí)也是空指針情形的一種常見解決方案,空的時(shí)候返回一個(gè)默認(rèn)值/占位符有决,不空的時(shí)候返回實(shí)際值闸拿。
可以看到還加了個(gè)isValidInstance的判斷函數(shù),原因是實(shí)際邏輯操作的時(shí)候书幕,可能占位符/默認(rèn)值并不能執(zhí)行新荤,所以要在所有的對(duì)外方法中添加實(shí)例檢查。
這樣修改后台汇,其實(shí)還是不可避免地要進(jìn)行多處修改苛骨,但這次的多處修改都是在當(dāng)前單例類里面的苟呐,不會(huì)涉及外部類痒芝;同時(shí)也是要保證之后當(dāng)前單例類里新增的對(duì)外實(shí)例方法,都要進(jìn)行實(shí)例驗(yàn)證才能進(jìn)行邏輯操作牵素,但是這仍舊是當(dāng)前類里的修改严衬,不涉及外部類。
這個(gè)解決方案在我看來笆呆,雖然還有類似的限制(要改動(dòng)多處请琳,對(duì)之后的邏輯有要求),但是還是比第一種解決方案要優(yōu)秀赠幕,因?yàn)橥獠空{(diào)用處有可能不是同一個(gè)開發(fā)者書寫的邏輯俄精;但把限制都約束在了同一個(gè)類里,既避免了空指針發(fā)散榕堰,也在很多情況下是同一個(gè)開發(fā)者維護(hù)的邏輯竖慧,更能避免后續(xù)Bug的產(chǎn)生。
看回PLACEHOLDER這個(gè)靜態(tài)變量逆屡,它會(huì)不會(huì)有可能為null呢圾旨?
不會(huì)。
這個(gè)變量的賦值是在類初始化的時(shí)候康二,這個(gè)值為null的話碳胳,只有可能是相關(guān)的ClassLoader都被銷毀了,否則只要這個(gè)類有加載到ClassLoader中初始化沫勿,這個(gè)變量就都不會(huì)為null挨约。詳細(xì)參見參考資料2味混。
參考資料