什么是單例模式?
單例模式是一種軟件設計模式,它確保一個類只有一個實例,并提供一個全局訪問點來訪問該實例。在單例模式中,類自身負責創建自己的唯一實例,并且保證在整個應用程序中只能訪問到這個實例。
實現步驟:
- 私有化構造方法;
- 私有化構造方法,防止通過new 創建對象。
- 提供靜態方法入口;
- 對外提供一個公開的靜態方法,獲取類的唯一實例入口。
- 定義成靜態是為了能通過類訪問,因為私有化構造方法,外界無法獲取實例對象,所以不能通過實例對象訪問。
- 定義靜態變量;
- 定義一個私有的單例類的靜態變量,在類加載的時候,初始化靜態變量。
- 為什么是私有的:
- 保護單例類的安全性,禁止外界對單例類的改變。
- 保證單例類的唯一性,不能修改唯一實例對象。
- 為什么是靜態的:
- 因為入口方法是靜態的,靜態方法中只能訪問靜態成員變量。
- 也符合靜態的特征,在類加載時,初始化靜態變量,且只執行一次。
實現(餓漢式):
? ? ? ? 1. 提前創建好單例實例對象。
? ? ? ? 2. 調用時,直接返回創建好的單例實例對象。
/**
* 單例模式:
* 餓漢式
*/
public class Singleton {private String name;// 唯一實例對象, 靜態變量private static Singleton singleton = new Singleton();// 私有化構造方法; 外界就不能創建當前類對象了。private Singleton(){}// 提供一個外界可以獲取唯一實例的入口public static Singleton getInstance(){return singleton;}public String getName(){return name;}public void setName(String name){this.name = name;}
}
實現(懶漢式):
? ? ? ? 1. 在用到時,才創建單例實例對象。
? ? ? ? 2. 調用時,判斷是否已經創建過單例實例對象,如果創建過直接返回,否則創建再返回。
/**
* 單例模式:
* 懶漢式
*/
public class Singleton {private String name;// 唯一實例對象,但是不初始化private static Singleton singleton;// 訪問入口,獲取單例實例對象的唯一入口。public static Singleton getInstance(){// 如果對象為空,說明時第一次訪問,創建單例實例對象。if( singleton == null){singleton = new Singleton();}return singleton;}public String getName(){reutnr name;}public void setName(String name){this.name = name;}
}
多線程環境實現:
public class Singleton {private static volatile Singleton instance;private Singleton() {// 私有構造函數}public static Singleton getInstance() {if (instance == null) {synchronized (Singleton.class) {if (instance == null) {instance = new Singleton();}}}return instance;}protected Object readResolve() {return getInstance();}
}
在上述代碼中,通過添加volatile
關鍵字修飾instance
變量,確保多線程環境下對該變量的可見性和有序性,避免了線程安全問題。
在靜態方法getInstance()
中,首先檢查instance
是否為null,如果為null才會進入同步塊。在同步塊內部再次判斷instance
是否為null,這是為了防止多個線程同時通過第一個判斷,并同時進入同步塊創建實例,從而導致多個實例的產生。
此外,還需要注意到readResolve()
方法的存在。當單例類被序列化后再進行反序列化時,可能會生成新的實例,為了避免這種情況,可以添加readResolve()
方法,并在方法內返回單例實例。這樣可以確保反序列化后獲得的對象仍然是同一個實例。
這是一個完整版的單例模式示例代碼,通過雙重檢查鎖定和防止反射攻擊的處理,保證了單例類的線程安全性和唯一性。
餓漢式和懶漢式的區別:
餓漢式和懶漢式是兩種常見的單例模式實現方式,它們的區別主要體現在初始化時機和線程安全性上。
-
餓漢式:
- 在類加載的時候就創建了對象實例,無論是否使用都會創建。
- 在類加載過程中就完成了實例化,因此不存在多線程并發的問題。
- 可能會浪費一些資源,因為不管有沒有使用到該實例,都會提前創建。
-
懶漢式:
- 延遲實例化,只有在第一次使用時才會創建對象實例。
- 在多線程環境下,需要考慮并發訪問的線程安全問題。
- 相比餓漢式,懶漢式在不使用時不會提前創建實例,節省了一些資源。
需要注意的是,在多線程環境下使用懶漢式時,需要進行額外的處理來保證線程安全性,以避免多個線程同時創建多個實例。常見的線程安全處理方式包括使用 synchronized 關鍵字、雙重檢查鎖定等。