前言
本片部落格主要記錄Java23種設計模式中的建立型模式中的單例模式。單例模式可分為兩類,一種是餓漢式,一種是懶漢式。餓漢式的三種設計方式(靜態變數方式、靜態程式碼塊方式、列舉方式),懶漢式(單鎖檢查方式、雙鎖檢查方式、靜態內部類方式),以及破壞單例模式的兩種方式:序列化反序列化,反射。
設計模式,是一套被反覆使用、多數人知曉的、經過分類編目的、程式碼設計經驗的總結。使用設計模式是為了可重用程式碼、讓程式碼更容易被他人理解、保證程式碼可靠性、程式的重用性
單例模式結構
私有的構造方法【核心】
私有的、靜態的例項化變數應用
提供一個公有的、靜態的獲取類例項物件方法
單例模式
單例模式(Singleton Pattern)是 Java 中最簡單的設計模式之一。它提供了一種建立物件的最佳方式。
單例模式涉及到一個單一的類,該類負責建立自己的物件,同時確保只有單個物件被建立。這個類提供了一種訪問其唯一的物件的方式,可以直接訪問,不需要例項化該類的物件。
1、單例類只能有一個例項。
2、單例類必須自己建立自己的唯一例項。
3、單例類必須給所有其他物件提供這一例項。
餓漢式
靜態變數方式
- 直接在建立物件時賦值
package hello;
public class Hello {
public static void main(String[] args) {
//只能通過getSingleton方法獲取,不能通過new方法建立
Singleton singleton = Singleton.getSingleton();
Singleton singleton11 = Singleton.getSingleton();
//通過hashCode檢視是否是同一個物件
System.out.println(singleton.hashCode());
System.out.println(singleton11.hashCode());
}
}
class Singleton{
//私有構造方法,這樣外界就不能建立了
private Singleton(){
}
//自己建立一個物件
private static Singleton singleton = new Singleton();
//給外界提供一個方法用於訪問
public static Singleton getSingleton(){
return singleton;
}
}
靜態程式碼塊方式
- 在靜態程式碼塊裡賦值
package hello;
public class Hello {
public static void main(String[] args) {
//只能通過getSingleton方法獲取,不能通過new方法建立
Singleton singleton = Singleton.getSingleton();
Singleton singleton11 = Singleton.getSingleton();
//通過hashCode檢視是否是同一個物件
System.out.println(singleton.hashCode());
System.out.println(singleton11.hashCode());
}
}
class Singleton{
//私有構造方法,這樣外界就不能建立了
private Singleton(){
}
//自己建立一個物件,但是不例項
private static Singleton singleton;
//通過靜態程式碼塊賦值
static {
singleton = new Singleton();
}
//給外界提供一個方法用於訪問
public static Singleton getSingleton(){
return singleton;
}
}
列舉方式
由於上面檢測程式碼相同,就不在這裡重複複製了。
只需要把class Singleton改為下面就行了
enum Singleton{
SINGLETON;
}
懶漢式
單鎖檢查模式
package hello;
public class Hello {
public static void main(String[] args) {
Singleton singleton = Singleton.getSingleton();
Singleton singleton11 = Singleton.getSingleton();
//通過hashCode檢視是否是同一個物件
System.out.println(singleton.hashCode());
System.out.println(singleton11.hashCode());
}
}
class Singleton{
//私有構造方法,這樣外界就不能建立了
private Singleton(){ }
//自己宣告一個物件
private static Singleton singleton;
//給外界提供一個方法用於訪問
public static synchronized Singleton getSingleton(){
//判讀singleton是否為null,如果是null就建立,否者直接返回
if (singleton == null){
singleton = new Singleton();
}
return singleton;
}
}
雙重檢查鎖模式
上面的驗證都是一樣的,這裡只顯示Singleton類就行
class Singleton{
//私有構造方法,這樣外界就不能建立了
private Singleton(){ }
//自己宣告一個物件
private static volatile Singleton singleton;
//給外界提供一個方法用於訪問
public static synchronized Singleton getSingleton(){
//判讀singleton是否為null,如果是null就建立,否者直接返回
if (singleton == null){
synchronized (Singleton.class){
if (singleton == null){
singleton = new Singleton();
}
}
}
return singleton;
}
}
靜態內部類實現
-
靜態內部類單例模式是一種優秀的單例模式,是比較常用的單例模式。在沒有加任何鎖時保證執行緒安全,並且沒有任何效能影響和空間浪費。
-
在載入Singleton時不會初始化singleton,只有第一次呼叫getSingleton()時。JVM載入SingletonHolder初始化singleton。
class Singleton{
//私有構造方法,這樣外界就不能建立了
private Singleton(){ }
//定義一個靜態內部類
private static class SingletonHolder{
//只會初始化一次
private static final Singleton singleton= new Singleton();
}
//給外界提供一個方法用於訪問
public static synchronized Singleton getSingleton(){
return SingletonHolder.singleton;
}
}
破壞單例模式
- 破壞單例模式的方式有兩種一種是序列化反序列化,另一種是反射,這裡我們指記錄反射
- 道高一尺,魔高一丈。有模式就會有破壞,有破壞還會有防破壞,但是還有反反破壞。這裡面就多了。
通過反射破壞單例模式
package hello;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
public class Hello {
public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
//獲取Singleton位元組碼物件
Class<Singleton> singletonClass = Singleton.class;
//獲取無參構造方法
Constructor<Singleton> declaredConstructor = singletonClass.getDeclaredConstructor();
//取消訪問檢查
declaredConstructor.setAccessible(true);
//建立Singleton物件
Singleton singleton = (Singleton) declaredConstructor.newInstance();
Singleton singleton1 = (Singleton) declaredConstructor.newInstance();
//通過hashCode檢視是否是同一個物件
System.out.println(singleton.hashCode());
System.out.println(singleton1.hashCode());
}
}
class Singleton{
//私有構造方法,這樣外界就不能建立了
private Singleton(){ }
//定義一個靜態內部類
private static class SingletonHolder{
//只會初始化一次
private static final Singleton singleton= new Singleton();
}
//給外界提供一個方法用於訪問
public static synchronized Singleton getSingleton(){
return SingletonHolder.singleton;
}
}
單例模式優缺點
優點:
- 單例模式在記憶體中只有一個例項,減少記憶體開支,特別是一個物件需要頻繁地建立銷燬時,而且建立或銷燬時效能又無法優化,單例模式就非常明顯了
- 單例模式只生成一個例項,減少系統的效能開銷
- 單例模式可以避免對資源的多重佔用
- 單例模式可以在系統設定全域性的訪問點,優化和共享資源訪問
缺點: - 不適用於變化的物件
- 由於單例模式沒有抽象層,所以擴充套件困難
- 單例類的職責過重,在一定程度上違背了“單一職責原則”
- 單一職責原則:一個類,應該只有一個職責