首頁 > 軟體

Java 設計模式以虹貓藍兔的故事講解單例模式

2022-03-28 19:00:06

專欄介紹

【JAVA長虹鍵法】 主要講了23種設計模式,本系列專欄會以虹貓藍兔七俠傳的故事為例來給大家詳細分析所有模式,希望能給大家帶來幫助!

本期介紹

模式: 單例模式

案例: 虹貓藍兔造劍

什麼是單例模式

單例模式(Singleton Pattern)是 Java 中最簡單的設計模式之一。這種型別的設計模式屬於建立型模式,它提供了一種建立物件的最佳方式。 這種模式涉及到一個單一的類,該類負責建立自己的物件,同時確保只有單個物件被建立。這個類提供了一種存取其唯一的物件的方式,可以直接存取,不需要範例化該類的物件。

注意:

1、單例類只能有一個範例。

2、單例類必須自己建立自己的唯一範例。

3、單例類必須給所有其他物件提供這一範例。

單例模式大致分為懶漢式和餓漢式,接下來用案例分析

懶漢式一

是否 Lazy 初始化: 是

是否多執行緒安全:否

實現難度: 易

描述: 這種方式是最基本的實現方式,這種實現最大的問題就是不支援多執行緒。因為沒有加鎖 synchronized,所以嚴格意義上它並不算單例模式。

這種方式 lazy loading 很明顯,不要求執行緒安全,在多執行緒不能正常工作。

概念看不懂沒關係,接下來舉例。

案例一:

建立一個劍類,這個類可以範例化一把劍。

虹貓和藍兔兩個人都想要造一把劍,虹貓先打造了一把劍,命名為長虹劍,然後藍兔也打造了一把劍,但是沒有命名。

現在來分析兩個情況,一個情況是正常模式,另一種情況是單例模式。

正常模式

劍類:

public class Jians {
    private String name;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}

測試類:

public class Demo1 {
    public static void main(String[] args) {
         //虹貓的劍
        Jians hong = new Jians();
         //藍兔的劍
        Jians lan = new Jians();
        hong.setName("長虹劍");
        System.out.println(hong.getName());
        System.out.println(lan.getName());
    }
}

結果:

在正常模式下,我new了虹貓和藍兔的劍。其實就是兩把劍,兩個不同的物件。

單例模式

劍類:

劍類中的getInstance()方法有一個造劍的功能,也就是new一個劍物件的功能。

 public class Jian {
    private static Jian jian;
    private String name;

    public static Jian getInstance() {
        if (jian == null) {
            jian = new Jian();
        }
        return jian;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}

測試類:

public class Demo {
    public static void main(String[] args) {
        //虹貓的劍
        Jian hong = Jian.getInstance();
        //藍兔的劍
        Jian lan = Jian.getInstance();
        //虹貓把劍命名長虹劍
        hong.setName("長虹劍");
        //輸出
        System.out.println(hong.getName());
        System.out.println(lan.getName());
    }
}

結果:

在單例模式下,我new了虹貓和藍兔的劍。其實就是一把劍,一個相同的物件。

為什麼執行緒不安全呢

單例模式下的劍類:

 public class Jian {
    private static Jian jian;
    private String name;

    public static Jian getInstance() {
        if (jian == null) {
            jian = new Jian();
        }
        return jian;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}

就拿這個類來說,他是執行緒不安全的。因為他通過getInstance()方法來獲取物件。如果是多執行緒執行,有執行緒1和執行緒2都先後進入了這個方法,因為執行緒1剛進入方法還沒有返回物件,執行緒2就進入了方法。所以執行緒2也會new一個物件,因為此時執行緒2進入方法的時候jian還是null的。

懶漢式二

是否 Lazy 初始化: 是

是否多執行緒安全: 是

實現難度: 易

描述: 這種方式具備很好的 lazy loading,能夠在多執行緒中很好的工作,但是,效率很低,99% 情況下不需要同步。

優點: 第一次呼叫才初始化,避免記憶體浪費。

缺點: 必須加鎖 synchronized 才能保證單例,但加鎖會影響效率。

為什麼執行緒安全呢

這裡還用上面那個案例,這次主要介紹懶漢式一和懶漢式二的區別。

懶漢式一和懶漢式二的主要區別在劍類上。

懶漢式一的劍類:

 public class Jian {
    private static Jian jian;
    private String name;

    public static Jian getInstance() {
        if (jian == null) {
            jian = new Jian();
        }
        return jian;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}

懶漢式二的劍類:

 public class Jian {
    private static Jian jian;
    private String name;

    public static synchronized Jian getInstance() {
        if (jian == null) {
            jian = new Jian();
        }
        return jian;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}

在getInstance()方法前加了個欄位synchronized,synchronized是一把鎖,作用是同一時間只能有一個執行緒進入這個方法,這樣就避免了懶漢式一中兩個執行緒都進入方法的情況出現,就不會new兩個物件,所以執行緒安全。

餓漢式

是否 Lazy 初始化: 否

是否多執行緒安全: 是

實現難度: 易

描述: 這種方式比較常用,但容易產生垃圾物件。

優點: 沒有加鎖,執行效率會提高。

缺點: 類載入時就初始化,浪費記憶體。

餓漢式就是直接在類中new一個物件,就是不管你需不需要劍我已經把劍造好了。

餓漢式劍類:

public class Jian {
    private static Jian jian=new Jian();
    private String name;

    public static synchronized Jian getInstance() {
        return jian;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}

測試類:

public class Demo {
    public static void main(String[] args) {
        //虹貓的劍
        Jian hong = Jian.getInstance();
        //藍兔的劍
        Jian lan = Jian.getInstance();
        //虹貓把劍命名長虹劍
        hong.setName("長虹劍");
        //輸出
        System.out.println(hong.getName());
        System.out.println(lan.getName());
    }
}

餓漢式中劍類的getInstance()方法已經失去了造劍的功能,測試類呼叫它只是返回一把提前造好的劍

懶漢式與餓漢式的區別

還是這個案例,虹貓和藍兔都想造一把劍,懶漢式是有一個劍類,劍類中有一個造劍的方法,呼叫這個方法的時候打造一把劍。餓漢式也有一個劍類,不同的是這個劍類直接就把劍造好了,沒有造劍的方法,不管你虹貓和藍兔想不想造劍,劍都已經造好了。

優點: 沒有加鎖,執行效率會提高。

缺點: 類載入時就初始化,浪費記憶體。

現在在看餓漢式的優缺點就容易理解了。

下期預告

模式: 簡單工廠模式

案例: 虹貓藍兔莎莉找鑄劍師造劍

到此這篇關於Java 設計模式以虹貓藍兔的故事講解單例模式的文章就介紹到這了,更多相關Java 單例模式內容請搜尋it145.com以前的文章或繼續瀏覽下面的相關文章希望大家以後多多支援it145.com!


IT145.com E-mail:sddin#qq.com