Java?設(shè)計(jì)模式以虹貓藍(lán)兔的故事講解單例模式
專欄介紹
【JAVA長虹鍵法】 主要講了23種設(shè)計(jì)模式,本系列專欄會以虹貓藍(lán)兔七俠傳的故事為例來給大家詳細(xì)分析所有模式,希望能給大家?guī)韼椭?/p>
本期介紹
模式: 單例模式
案例: 虹貓藍(lán)兔造劍
什么是單例模式
單例模式(Singleton Pattern)是 Java 中最簡單的設(shè)計(jì)模式之一。這種類型的設(shè)計(jì)模式屬于創(chuàng)建型模式,它提供了一種創(chuàng)建對象的最佳方式。 這種模式涉及到一個單一的類,該類負(fù)責(zé)創(chuàng)建自己的對象,同時確保只有單個對象被創(chuàng)建。這個類提供了一種訪問其唯一的對象的方式,可以直接訪問,不需要實(shí)例化該類的對象。
注意:
1、單例類只能有一個實(shí)例。
2、單例類必須自己創(chuàng)建自己的唯一實(shí)例。
3、單例類必須給所有其他對象提供這一實(shí)例。
單例模式大致分為懶漢式和餓漢式,接下來用案例分析
懶漢式一
是否 Lazy 初始化: 是
是否多線程安全:否
實(shí)現(xiàn)難度: 易
描述: 這種方式是最基本的實(shí)現(xiàn)方式,這種實(shí)現(xiàn)最大的問題就是不支持多線程。因?yàn)闆]有加鎖 synchronized,所以嚴(yán)格意義上它并不算單例模式。
這種方式 lazy loading 很明顯,不要求線程安全,在多線程不能正常工作。
概念看不懂沒關(guān)系,接下來舉例。
案例一:
創(chuàng)建一個劍類,這個類可以實(shí)例化一把劍。
虹貓和藍(lán)兔兩個人都想要造一把劍,虹貓先打造了一把劍,命名為長虹劍,然后藍(lán)兔也打造了一把劍,但是沒有命名。
現(xiàn)在來分析兩個情況,一個情況是正常模式,另一種情況是單例模式。
正常模式
劍類:
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(); //藍(lán)兔的劍 Jians lan = new Jians(); hong.setName("長虹劍"); System.out.println(hong.getName()); System.out.println(lan.getName()); } }
結(jié)果:
在正常模式下,我new了虹貓和藍(lán)兔的劍。其實(shí)就是兩把劍,兩個不同的對象。
單例模式
劍類:
劍類中的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(); //藍(lán)兔的劍 Jian lan = Jian.getInstance(); //虹貓把劍命名長虹劍 hong.setName("長虹劍"); //輸出 System.out.println(hong.getName()); System.out.println(lan.getName()); } }
結(jié)果:
在單例模式下,我new了虹貓和藍(lán)兔的劍。其實(shí)就是一把劍,一個相同的對象。
為什么線程不安全呢
單例模式下的劍類:
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; } }
就拿這個類來說,他是線程不安全的。因?yàn)樗ㄟ^getInstance()方法來獲取對象。如果是多線程運(yùn)行,有線程1和線程2都先后進(jìn)入了這個方法,因?yàn)榫€程1剛進(jìn)入方法還沒有返回對象,線程2就進(jìn)入了方法。所以線程2也會new一個對象,因?yàn)榇藭r線程2進(jìn)入方法的時候jian還是null的。
懶漢式二
是否 Lazy 初始化: 是
是否多線程安全: 是
實(shí)現(xiàn)難度: 易
描述: 這種方式具備很好的 lazy loading,能夠在多線程中很好的工作,但是,效率很低,99% 情況下不需要同步。
優(yōu)點(diǎn): 第一次調(diào)用才初始化,避免內(nèi)存浪費(fèi)。
缺點(diǎn): 必須加鎖 synchronized 才能保證單例,但加鎖會影響效率。
為什么線程安全呢
這里還用上面那個案例,這次主要介紹懶漢式一和懶漢式二的區(qū)別。
懶漢式一和懶漢式二的主要區(qū)別在劍類上。
懶漢式一的劍類:
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是一把鎖,作用是同一時間只能有一個線程進(jìn)入這個方法,這樣就避免了懶漢式一中兩個線程都進(jìn)入方法的情況出現(xiàn),就不會new兩個對象,所以線程安全。
餓漢式
是否 Lazy 初始化: 否
是否多線程安全: 是
實(shí)現(xiàn)難度: 易
描述: 這種方式比較常用,但容易產(chǎn)生垃圾對象。
優(yōu)點(diǎn): 沒有加鎖,執(zhí)行效率會提高。
缺點(diǎn): 類加載時就初始化,浪費(fèi)內(nèi)存。
餓漢式就是直接在類中new一個對象,就是不管你需不需要劍我已經(jīng)把劍造好了。
餓漢式劍類:
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(); //藍(lán)兔的劍 Jian lan = Jian.getInstance(); //虹貓把劍命名長虹劍 hong.setName("長虹劍"); //輸出 System.out.println(hong.getName()); System.out.println(lan.getName()); } }
餓漢式中劍類的getInstance()方法已經(jīng)失去了造劍的功能,測試類調(diào)用它只是返回一把提前造好的劍
懶漢式與餓漢式的區(qū)別
還是這個案例,虹貓和藍(lán)兔都想造一把劍,懶漢式是有一個劍類,劍類中有一個造劍的方法,調(diào)用這個方法的時候打造一把劍。餓漢式也有一個劍類,不同的是這個劍類直接就把劍造好了,沒有造劍的方法,不管你虹貓和藍(lán)兔想不想造劍,劍都已經(jīng)造好了。
優(yōu)點(diǎn): 沒有加鎖,執(zhí)行效率會提高。
缺點(diǎn): 類加載時就初始化,浪費(fèi)內(nèi)存。
現(xiàn)在在看餓漢式的優(yōu)缺點(diǎn)就容易理解了。
下期預(yù)告
模式: 簡單工廠模式
案例: 虹貓藍(lán)兔莎莉找鑄劍師造劍
到此這篇關(guān)于Java 設(shè)計(jì)模式以虹貓藍(lán)兔的故事講解單例模式的文章就介紹到這了,更多相關(guān)Java 單例模式內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
IDEA+Maven創(chuàng)建Spring項(xiàng)目的實(shí)現(xiàn)步驟
這篇文章主要介紹了IDEA+Maven創(chuàng)建Spring項(xiàng)目的實(shí)現(xiàn)步驟,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2020-07-07使用SpringCache進(jìn)行緩存數(shù)據(jù)庫查詢方式
這篇文章主要介紹了使用SpringCache進(jìn)行緩存數(shù)據(jù)庫查詢方式,具有很好的參考價值,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2021-10-10Java并發(fā)容器ConcurrentLinkedQueue解析
這篇文章主要介紹了Java并發(fā)容器ConcurrentLinkedQueue解析,2023-12-12Springboot使用Spring Data JPA實(shí)現(xiàn)數(shù)據(jù)庫操作
Spring Data JPA 是 Spring 基于 Spring Data 框架、在JPA 規(guī)范的基礎(chǔ)上開發(fā)的一個框架,使用 Spring Data JPA 可以極大地簡化JPA 的寫法,本章我們將詳細(xì)介紹在Springboot中使用 Spring Data JPA 來實(shí)現(xiàn)對數(shù)據(jù)庫的操作2021-06-06Spring5學(xué)習(xí)之基礎(chǔ)知識總結(jié)
這篇文章主要介紹了Spring5學(xué)習(xí)之基礎(chǔ)知識總結(jié),文中有非常詳細(xì)的代碼示例,對正在學(xué)習(xí)java的小伙伴們有非常好的幫助,需要的朋友可以參考下2021-05-05