單例模式與靜態類


 

單例模式:是一種常用的創建對象的方式,比起普通方式創建對象,這種思想具有非常多的優點。是java 23種設計模式之一。

構造器被私有化,類中對外提供唯一對象,常見的有懶漢式和餓漢式等。

懶漢式:調用時創建一個對象,后續調用不再重新創建。

餓漢式:類加載時就創建了,並且只創建一次。

 

靜態類(常見的工具類都是):所有的成員都是靜態的。可以認為是靜態的集合。

例如:

Math類是一個靜態類。

我們使用Math.round()方法時,並不關注Math類本身,關注的其實是round方法的方法體中的代碼執行邏輯。

使用工具類,其實是使用了工具類中方法的方法體。與類對象無關。

 

所以單例模式與靜態類的區別在於:

單例模式是面向對象的設計。本質上還是創建對象,調用方法。單例存在的根本就是為了得到對象。

靜態類是單純使用方法體,對象沒有存在的價值。所以直接使用類名調用,不創建對象。靜態類存在是為了快捷方便的使用里面的方法。

二者設計目標不同。

靜態類構造函數沒有存在的價值,單例模式需要獲取對象,所以必須有構造器,但是私有化。而且可以有非靜態的成員。(單例模式推薦使用靜態的成員)

 

單例模式:

第一種:

懶漢式①,線程不安全。

實例

public class Singleton {

private static Singleton instance;

private Singleton (){}

public static Singleton getInstance() {

if (instance == null) {

instance = new Singleton();

}

return instance;

}

}

一句話:先定義變量,不初始化。等到使用時初始化。但是多線程時有問題。

 

第二種:

懶漢式,線程安全

優點:第一次調用才初始化,避免內存浪費。
缺點:必須加鎖 synchronized 才能保證單例,但加鎖會影響效率。

實例

public class Singleton {

private static Singleton instance;

private Singleton (){}

public static synchronized Singleton getInstance() {

if (instance == null) {

instance = new Singleton();

} return instance;

}

}

一句話:先定義變量,不初始化。等到使用時初始化。加鎖了,多線程也可以很好地使用。但是效率低。

 

第三種:

餓漢式③,線程安全。

這種方式比較常用,但容易產生垃圾對象。
優點:沒有加鎖,執行效率會提高。
缺點:類加載時就初始化,浪費內存。

基於 classloader 機制避免了多線程的同步問題,不過,instance 在類裝載時就實例化,雖然導致類裝載的原因有很多種,在單例模式中大多數都是調用 getInstance 方法, 但是也不能確定有其他的方式(或者其他的靜態方法)導致類裝載,這時候初始化 instance 顯然沒有達到 lazy loading 的效果。

實例

public class Singleton {

private static Singleton instance = new Singleton();

private Singleton (){}

public static Singleton getInstance() {

return instance;

}

}

一句話:類加載時就創建對象。類被加載的可能性太高,有時候類被加載不一定需要使用到對象,導致內存垃圾產生。

 

第四種:

雙檢鎖/雙重校驗鎖(DCL,即 double-checked locking)

是否 Lazy 初始化:是

是否多線程安全:是

這種方式采用雙鎖機制,安全且在多線程情況下能保持高性能。
getInstance() 的性能對應用程序很關鍵。

實例

public class Singleton {

private volatile static Singleton singleton;

private Singleton (){}

public static Singleton getSingleton() {

if (singleton == null) {

synchronized (Singleton.class) {

if (singleton == null) {

singleton = new Singleton();

}

}

}

return singleton;

}

}

一句話:調用時才創建,但是雙重鎖機制,既多線程安全,也保證高效。

 

第五種:

登記式/靜態內部類

是否 Lazy 初始化:是

是否多線程安全:是

這種方式能達到雙檢鎖方式一樣的功效,但實現更簡單。對靜態域使用延遲初始化,應使用這種方式而不是雙檢鎖方式。這種方式只適用於靜態域的情況,雙檢鎖方式可在實例域需要延遲初始化時使用。
這種方式同樣利用了 classloader 機制來保證初始化 instance 時只有一個線程,它跟第 3 種方式不同的是:第 3 種方式只要 Singleton 類被裝載了,那么 instance 就會被實例化(沒有達到 lazy loading 效果),而這種方式是 Singleton 類被裝載了,instance 不一定被初始化。因為 SingletonHolder 類沒有被主動使用,只有通過顯式調用 getInstance 方法時,才會顯式裝載 SingletonHolder 類,從而實例化 instance。想象一下,如果實例化 instance 很消耗資源,所以想讓它延遲加載,另外一方面,又不希望在 Singleton 類加載時就實例化,因為不能確保 Singleton 類還可能在其他的地方被主動使用從而被加載,那么這個時候實例化 instance 顯然是不合適的。這個時候,這種方式相比第 3 種方式就顯得很合理。

實例

public class Singleton {

private static class SingletonHolder {

private static final Singleton INSTANCE = new Singleton();

}

private Singleton (){}

public static final Singleton getInstance() {

return SingletonHolder.INSTANCE;

}

}

一句話:內部類使用類似餓漢式方式,外部類的類方法對外返回對象。線程安全,節省內存。

 

 

 

文中部分內容節選自菜鳥教程:http://www.runoob.com/design-pattern/singleton-pattern.html


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM