單例模式與靜態(tài)類(一個類,所有方法為靜態(tài)方法)是另一個非常有趣的問題,由于單例模式和靜態(tài)類都具有良好的訪問性,它們之間有許多相似之處,例如,兩者可以直接使用而無須創(chuàng)建對象,都可提交唯一實例,在一個非常高的高度上看起來它們都為是用于同樣的任務。那么Java中單例模式與靜態(tài)的區(qū)別有哪些呢?
接下來,我們會了解什么Java中的什么地方使用單例模式,什么時候使用靜態(tài)類更好,順便說下,JDK中有幾個關于兩者的例子,并且這例子非常聰明的,例如java.lang.Math是一個final類,并且其方法為靜態(tài)方法,另一方面java.lang.Runtime是一個單例的類。對于那些不熟悉單例模式或靜態(tài)類的人,靜態(tài)類就是一個Java類,它僅包含靜態(tài)方法,一個非常好靜態(tài)類的例子就是java.lang.Math,它包括了許多為不同數據功能實現工具方法,例如sqrt(),而單例類,在整個應用生命周期內只有一個實例,例如java.lang.Runtime。
什么時候在Java中使用靜態(tài)類替換單例
確實存在一些場景,靜態(tài)類比單例更適合,這個場景中主要的一個例子就是java.lang.Math,它不是單例的,其所有方法都是靜態(tài)方法,這里我給出幾個場景,我覺得使用靜態(tài)類比單例模式更適合。
1)如果你的單例不需要維護任何狀態(tài),僅僅提供全局訪問的方法,這種情況考慮使用靜態(tài)類,靜態(tài)方法比單例更快,因為靜態(tài)的綁定是在編譯期就進行的。但是要記住,不建議在靜態(tài)類中維護狀態(tài)信息,特別是在并發(fā)環(huán)境中,若無適當的同步措施而修改多線程并發(fā)時,會導致壞的競態(tài)條件。
如果你需要將一些工具方法集中在一起時,你可以選擇使用靜態(tài)方法,但是別的東西,要求單例訪問資源時,應該使用單例模式。
Java中單例與靜態(tài)的區(qū)別
它們基本的區(qū)別一個表現類,一個表現方法,下面列幾條它們之間的區(qū)別。
1)靜態(tài)類比單例具有更好的性能,因為靜態(tài)方法在編譯期綁定。
2)再次,它們的區(qū)別是override的能力,因Java中的靜態(tài)方法是不可以覆蓋的,這就導致其木有太多的靈活性,另一面,你可通過繼承的方式覆蓋單例類中定義的方法。
3)靜態(tài)類很難模擬,因此難于單例測試,單例更容易模擬,因為也比靜態(tài)類易于編寫單元測試,不論什么單例期望什么,你都可以傳遞模擬對象,例如構造方法或方法參數。
4)如果你的需求中需要維護狀態(tài)信息,則單例比靜態(tài)類更適合,因為后者在維護狀態(tài)信息方面是非??膳碌?,并導致狡滑的bug。
5)如果是一個非常重的對象,單例可以懶加載,但是靜態(tài)類沒有這樣的優(yōu)勢,并且非常熱切的加載。
6)許多依賴注入的框架對單例都有良好的管理,例如Spring,使用它們非常容易。
兩者之間的這些區(qū)別,有助于我們在面對一些場景時做出選擇,下一節(jié)中了解什么時候選擇單例而不是靜態(tài)類。
Java中,選擇單例而不是靜態(tài)類的優(yōu)點
單例與靜態(tài)主要的優(yōu)點是前者比后者更具有面向對象的能力,使用單例,可以通過繼承和多態(tài)擴展基類,實現接口和更有能力提供不同的實現,如果我們討論java.lang.Runtime,在Java中它是單例,調用getRuntime()方法,會基于不同的JVM返回不同的實現,但也保證了每個JVM中實有一個實例,如果java.lang.Runtime是一個靜態(tài)類,不太可能因不同的JVM返回不同的實現。
這就是Java中單例與靜態(tài)類的區(qū)別,當你需要一個全OO能力的對象時,選擇單例,如果僅僅是將一些靜態(tài)方法預售,使用靜態(tài)類。