設計模式之單例模式(靜態屬性可稱爲單例思想的特殊運用?)

  最近看了看程傑前輩的《大話設計模式》,主要是上次面試被問設計模式,答的很菜。。。經過一段時間的沉澱,現在來談談我對其單例模式的理解。

一、單例模式

1、定義

  單例模式(Singleton),保證一個類僅有一個實例,並且提供全局訪問該實例的接口。簡單的說,既要保證該類只有一個實例,也要保證全局訪問的就是這個唯一的實例

2、使用場景

  那麼肯定有小夥伴會問,單例模式有啥使用場景嗎?限制一個類只有一個實例,這種場景比較少吧。。。
  確實,限制一個類只有一個實例的場景比較少,但是並不代表單例模式毫無用武之地。舉個栗子,運用程序是如何維持某個窗口只一個的?比如系統桌面右鍵菜單。
在這裏插入圖片描述
  如果不控制窗口對應的對象同一時刻只有一個,那麼可能你每點一次,都會彈出一個同樣的窗口。這顯然不符合實際邏輯,也會造成資源的浪費。

  其實還有另外一種普遍的場景,類中的靜態屬性。我想寫過面向對象編程序思想的程序小夥伴都在類中定義過static屬性,我們把某個變量加上static關鍵字修飾,目的是把這個變量聲明爲本身所有,而不是類的實例所有。最主要的作用不就是類的所有實例都來訪問這同一個static屬性,這正好與我們單例模式的思想吻合。

二、舉例(靜態屬性)

  上面提到了 類中的靜態屬性是單例模式思想的一種特殊運用,下面我們通過代碼來模擬一下。

public class SingleTon {
    // 對object屬性添加static修飾(這個變量屬於SingleTon類,不屬於實例)
    public static MyClass object = new MyClass();

    public static void main(String[] args) {
        // 新建兩個SingleTon對象
        SingleTon singleTonOne = new SingleTon();
        SingleTon singleTonTwo = new SingleTon();
        // 由於Java提供通過類實例訪問類靜態屬性的途徑,所以這裏我們測試一下
        // 兩個實例訪問的是否爲同一個實例object(這裏只是演示,實際編寫代碼千萬別通過對象訪問類靜態屬性!!!)
        System.out.println(singleTonOne.object);
        System.out.println(singleTonTwo.object);
    }
}

class MyClass{
    public MyClass(){

    }
}

在這裏插入圖片描述
  由於在Java語言中,每個類只能加載一次,因此SingleTon類中的object屬性只會被初始化一次,也就是隻有一個。通過觀察程序的輸出,確實發現兩個實例共用SingleTon類中的object靜態屬性,做到了只有一個實例。但這個程序不能稱爲嚴格的單例,因爲在任何地方都可以通過調用MyClass類的構造器來手動創建MyClass的實例,因此這提示我們需要修改單例對應的類的構造器訪問權限爲私有(private關鍵字修飾)。在閱讀下文前,可以思考一下如何改造這個程序,使MyClass變爲嚴格的單例。

三、兩種實現方式(Java)

  單例模式的實現,常見的方法有兩種,分別是懶漢式餓漢式,兩者都將類的構造器私有化,但是各有優缺點。

1、餓漢式

  將構造器私有化,並且用static屬性初始化一個實例。這樣每次就只能訪問同一個實例,並且限制了類的new操作(構造器被私有化了)。

public class SingleTonTest {
    public static void main(String[] args) {
        // 通過SingleTon.getInstance接口,兩次獲取SingleTon類的實例
        SingleTon singleTonOne = SingleTon.getInstance();
        SingleTon singleTonTwo = SingleTon.getInstance();
        // 打印兩次獲取的實例的內存地址
        System.out.println(singleTonOne);
        System.out.println(singleTonTwo);
    }
}

class SingleTon{
    // SingleTon靜態屬性,初始化時new一個對象(類中能使用該類聲明的private方法)
    private static SingleTon singleTon = new SingleTon();
    // 構造器私有了,外部不能new對象(保證了虛擬機中只能有一個實例)
    private SingleTon(){

    }
    // 對外展示訪問SingleTon實例的接口(保證了全局訪問的都是SingleTon類的同一個實例)
    public static SingleTon getInstance() {
        return singleTon;
    }
}

在這裏插入圖片描述
  該方式的特點就是在加載類的時候就new一個唯一的實例,以後直接通過相應的API獲取,繼而保證了實例唯一,並且全局訪問同一個實例。提高了使用效率,但是可能出現資源浪費的情況,假設整個程序運行過程中都沒訪問這個實例,那不是白白生成了一個實例。。。

2、懶漢式

  同樣將類的構造器私有化,但是對外展示的獲取實例的API智能生成實例,如果已經生成過了實例,直接放回之前的示例,否則生成一個新的實例。

public class SingleTonTest {
    public static void main(String[] args) {
        // 通過SingleTon.getInstance接口,兩次獲取SingleTon類的實例
        SingleTon singleTonOne = SingleTon.getInstance();
        SingleTon singleTonTwo = SingleTon.getInstance();
        // 打印兩次獲取的實例的內存地址
        System.out.println(singleTonOne);
        System.out.println(singleTonTwo);
    }
}

class SingleTon{
    // SingleTon靜態屬性,指向生成的SingleTon實例
    private static SingleTon singleTon = null;
    // 構造器私有了,外部不能new對象(保證了虛擬機中只能有一個實例)
    private SingleTon(){

    }
    // 對外展示訪問SingleTon實例的接口(保證了全局訪問的都是SingleTon類的同一個實例)
    // 可能存在線程不安全的情況(多個線程同時調用getInstance,此時可能生成多個實例),使用synchronized關鍵字修飾,默認是SingleTon.class鎖
    public static synchronized SingleTon getInstance() {
        if (singleTon == null) {
            // 爲null就new一個實例
            singleTon = new SingleTon();
        }
        return singleTon;
    }
}

在這裏插入圖片描述

  懶漢式中的字,就是由 先判斷是否生成過實例,再考慮是否生成實例而來。這種方式解決了餓漢式可能浪費資源的問題,但是降低了使用效率,因爲每次都需要加鎖、判斷是否爲null、釋放鎖。

四、總結

  單例模式是衆多設計模式中較爲簡單的一種,核心思想是保證某類有且只用一個實例對象,並且需要保證全局都只能訪問這個唯一的實例。
  經典的例子就是GUI中的窗口,同一個窗口類一般只能有一個對象。不過博主個人認爲,類中的static屬性是單例思想的一種特殊運用,因爲它保證了在本類中所用的對象訪問同一個static屬性(大家看看就行,別當真😂😂😂)。
在這裏插入圖片描述

資料參考:
《大話設計模式》程傑著 單例模式

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章