淺析單例模式--Java

airl發表於2022-03-24

前言

本片部落格主要記錄Java23種設計模式中的建立型模式中的單例模式。單例模式可分為兩類,一種是餓漢式,一種是懶漢式。餓漢式的三種設計方式(靜態變數方式、靜態程式碼塊方式、列舉方式),懶漢式(單鎖檢查方式、雙鎖檢查方式、靜態內部類方式),以及破壞單例模式的兩種方式:序列化反序列化,反射。
設計模式,是一套被反覆使用、多數人知曉的、經過分類編目的、程式碼設計經驗的總結。使用設計模式是為了可重用程式碼、讓程式碼更容易被他人理解、保證程式碼可靠性、程式的重用性

image

單例模式結構

私有的構造方法【核心】
私有的、靜態的例項化變數應用
提供一個公有的、靜態的獲取類例項物件方法

單例模式

單例模式(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;
    }

}

單例模式優缺點

優點:

  • 單例模式在記憶體中只有一個例項,減少記憶體開支,特別是一個物件需要頻繁地建立銷燬時,而且建立或銷燬時效能又無法優化,單例模式就非常明顯了
  • 單例模式只生成一個例項,減少系統的效能開銷
  • 單例模式可以避免對資源的多重佔用
  • 單例模式可以在系統設定全域性的訪問點,優化和共享資源訪問
    缺點:
  • 不適用於變化的物件
  • 由於單例模式沒有抽象層,所以擴充套件困難
  • 單例類的職責過重,在一定程度上違背了“單一職責原則”
  • 單一職責原則:一個類,應該只有一個職責

相關文章