這篇我們學習的是單例模式,相信很多朋友都或多或少使用過這個模式。很多設計模式的入門,都把單例模式作為第一個的,但是因為我們是跟著書本學習,所以放在了第五個裡面。那麼,你使用過的單例模式是怎麼樣的呢?懶漢式?餓漢式?雙重校驗?靜態? 先來看下定義,單例模式(Singleton Pattern): 用來 ...
這篇我們學習的是單例模式,相信很多朋友都或多或少使用過這個模式。很多設計模式的入門,都把單例模式作為第一個的,但是因為我們是跟著書本學習,所以放在了第五個裡面。那麼,你使用過的單例模式是怎麼樣的呢?懶漢式?餓漢式?雙重校驗?靜態?
先來看下定義,單例模式(Singleton Pattern):用來創建獨一無二的,只能有一個實例的對象的入場券。而且,單例模式的類圖是所有設計模式中最簡單的,事實上只有一個類。但是,儘管從類的設計上來說簡單,實現上還是會遇見相當多的波折噢。
單例模式有什麼用處?
有些對象其實我們只需要一個,比方說:線程池、緩存、對話框、處理偏好設置和註冊表對象、日誌對象等。事實上,這些對象只能以一個實例,如果製造出多個實例,就會導致許多問題的產生,例如:程式的行為異常、資源使用過量,或者就是不一致的結果。
剖析經典的單例模式實現
我們先來看下經典的單例模式的實現代碼:
public class Singleton {
private static Singleton uniqueInstance;
private Singleton() {}
public static Singleton getInstance() {
if (uniqueInstance == null) {
uniqueInstance = new Singleton();
}
return uniqueInstance;
}
}
- 利用一個靜態變數來記錄Singleton類的唯一實例
- 把構造器聲明為私有化,只有Singleton類才可以調用構造器
- 用getInstance()方法實例化對象,並返回這個實例
再仔細看下getInstance()方法,這裡需要著重描述下。
- uniqueInstance 擁有一個“實例”,而且是一個靜態變數
- 如果uniqueInstance是空的,表示還沒有創建實例
- 如果不存在,我們就利用私有的構造器產生一個Singleton實例並把它賦值到uniqueInstance靜態變數中。請註意,如果我們不需要這個實例,他就永遠不會產生。這就是“延遲實例化”(lazy instaniaze)
- 如果uniqueInstance不是null,就表示之前已經創建過對象,我們就直接返回
- 當執行到return語句,表示我們已經有實例,並將uniqueInstance當返回值
如果沒有單例模式,這裡有一個代碼寫的很小心的例子,看完你肯定會感受到單例模式的重要性。
上圖中的公司有意識地防止不好的事情發生,對吧。但是,如果防不勝防,同事存在兩個ChocolateBoiler實例,可能將發生很糟糕的事情。那麼,如果有過個ChocolateBoiler實例存在,可能發生什麼嚴重的事情呢?咋這個例子上,就是會產生資源的浪費,原料的溢出等等。
那麼,你能根據經典的單例模式,寫出這個巧克力工廠的單例模式嗎?我們晚點揭曉。
定義單例模式
單例模式:確保一個類只有一個實例,並提供一個全局訪問點。
這定義一點兒都不讓人吃驚,但是讓我們更深入一點兒:
- 到底怎麼回事?我們正在把某個類設計成自己管理的一個單獨實例,同時也避免其他類再自行產生實例。要想取得單例實例,通過單例類是唯一的途徑
- 我們也提供對這個實例的全局訪問點:當你需要實例時,向類查詢,他會返回單個實例。前面的例子利用延遲實例化的方式創建單例,這種做法對資源敏感的對象特別重要。
那我們來看看單例的類圖:
你看吧,之前就說過,這個單例模式只有一個類圖,是不是很簡單呢?仔細看看他吧。
但是,這些都只是單線程模式下的單例模式,參考上面這個巧克力工廠,如果是多線程模式下的單例,那又會是什麼樣的呢?經典的單例模式,能確保你在單線程下不出問題,但是,我們想要讓人家效率更高,產量更大,勢必需要多線程?
那麼,請屏幕前的你,先好好想想,我們下次學習的時候,通過JVM原理,把這個煩惱給解決了。今天的學習就先到這裡啦。