欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

一文帶你搞懂Java單例模式

 更新時(shí)間:2022年11月04日 08:16:54   作者:UnicornLien  
單例就是單實(shí)例的意思,即在系統(tǒng)全局,一個(gè)類(lèi)只創(chuàng)建一個(gè)對(duì)象,并且在系統(tǒng)全局都可以訪問(wèn)這個(gè)對(duì)象而不用重新創(chuàng)建。本文將通過(guò)示例為大家詳細(xì)講解Java單例模式的使用,需要的可以參考一下

在創(chuàng)建型設(shè)計(jì)模式中,我們第一個(gè)學(xué)習(xí)的是單例模式(Singleton Pattern),這是設(shè)計(jì)模式中最簡(jiǎn)單的模式之一。

單例是什么意思呢?

單例就是單實(shí)例的意思,即在系統(tǒng)全局,一個(gè)類(lèi)只創(chuàng)建一個(gè)對(duì)象,并且在系統(tǒng)全局都可以訪問(wèn)這個(gè)對(duì)象而不用重新創(chuàng)建。

一、單例模式的基本寫(xiě)法

單例模式示例代碼:

public class Singleton {
 
  //	Singleton類(lèi)自己持有這個(gè)單例對(duì)象
   private static Singleton instance = new Singleton();
 
   //	構(gòu)造方法設(shè)置為私有,避免在Singleton類(lèi)外部創(chuàng)建Singleton對(duì)象
   private Singleton() {}
 
   //	提供獲取單例對(duì)象的靜態(tài)方法
   public static Singleton getInstance() {
      return instance;
   }
 
   public void hello() {
      System.out.println("Hello!");
   }
}

使用:

Singleton obj = Singleton.getInstance();
obj.hello();

分析SingleObject類(lèi)的特征:

  • SingleObject類(lèi)的構(gòu)造方法是私有的,這樣可以保證只能在SingleObject類(lèi)內(nèi)部才能創(chuàng)建對(duì)象,而無(wú)法在類(lèi)外部創(chuàng)建SingleObject對(duì)象。
  • SingleObject類(lèi)中有一個(gè)instance成員屬性,它用來(lái)持有這個(gè)SingleObject對(duì)象。
  • SingleObject類(lèi)提供了一個(gè)靜態(tài)方法getInstance,它可以讓我們?cè)谌魏慰梢栽L問(wèn)到SingleObject類(lèi)的地方,都可以使用SingleObject.getInstance()來(lái)獲取到這個(gè)SingleObject對(duì)象。

二、單例模式的作用

單例模式有什么用呢?

1. 控制對(duì)象的數(shù)量

當(dāng)你編寫(xiě)了一個(gè)類(lèi)提供給其他人調(diào)用時(shí),對(duì)方看到是一個(gè)類(lèi),很有可能第一反應(yīng)是嘗試new一下。

你自己編寫(xiě)的類(lèi)你自己是清楚如何使用的,在整個(gè)系統(tǒng)內(nèi)這個(gè)類(lèi)只需要?jiǎng)?chuàng)建一個(gè)對(duì)象就夠了,但對(duì)方可能并不清楚。

這時(shí)候你可以把這個(gè)類(lèi)編寫(xiě)為單例形式,把構(gòu)造方法私有化,讓對(duì)方無(wú)法通過(guò)new來(lái)創(chuàng)建對(duì)象,只能使用getInstance來(lái)獲取。

這個(gè)模式可以幫助你有效的控制對(duì)象的數(shù)量,畢竟,有的類(lèi)其內(nèi)部實(shí)現(xiàn)復(fù)雜,如果頻繁創(chuàng)建銷(xiāo)毀對(duì)象,可能還是很耗費(fèi)服務(wù)器資源的。

2.全局訪問(wèn)

單例模式的特點(diǎn)是單例類(lèi)自己持有這個(gè)單例對(duì)象,并且提供一個(gè)靜態(tài)方法可在全局獲取到這個(gè)單例對(duì)象。

如果沒(méi)有單例模式的情況下,我們一般是在代碼A處創(chuàng)建這個(gè)對(duì)象,在代碼B處如果也要使用這個(gè)對(duì)象,就需要將這個(gè)對(duì)象進(jìn)行參數(shù)傳遞。為了避免傳來(lái)傳去,我們可能會(huì)寫(xiě)個(gè)Holder類(lèi),把這個(gè)對(duì)象放在Holder的成員變量中。

而單例模式的這個(gè)優(yōu)點(diǎn)是,我們可以避免這樣的困擾,直接從單例類(lèi)中獲取。

三、單例模式的變種

上面介紹的是單例模式的一種基本寫(xiě)法,實(shí)際我們還可以對(duì)其進(jìn)行優(yōu)化和變種。

1. 餓漢式

基本寫(xiě)法中,對(duì)象的創(chuàng)建是直接寫(xiě)在Singleton類(lèi)的成員屬性上的,因此當(dāng)Singleton類(lèi)被加載時(shí),就會(huì)立即創(chuàng)建Singleton對(duì)象,這個(gè)寫(xiě)法比較簡(jiǎn)單,但我們可能并不會(huì)馬上使用到這個(gè)Singleton對(duì)象,過(guò)早的創(chuàng)建會(huì)造成內(nèi)存資源浪費(fèi)。

這種一加載類(lèi)就急于創(chuàng)建對(duì)象的寫(xiě)法,我們稱(chēng)之為餓漢式。

如果對(duì)內(nèi)存資源不在意,那么其實(shí)餓漢式這個(gè)寫(xiě)法也就沒(méi)什么大的缺點(diǎn),而且寫(xiě)起來(lái)還簡(jiǎn)單,還是可以用的。

2. 懶漢式(線程不安全)

此變種僅是介紹,不要使用。

既然餓漢式在類(lèi)加載時(shí)就創(chuàng)建對(duì)象會(huì)造成內(nèi)存浪費(fèi),那么我們把創(chuàng)建對(duì)象這個(gè)步驟挪到要用時(shí)再創(chuàng)建不就好了?

我們要使用對(duì)象時(shí),都是通過(guò)getInstance方法先獲取對(duì)象,我們可以在getInstance方法中完成對(duì)象創(chuàng)建。

這種需要時(shí)再創(chuàng)建的寫(xiě)法,我們稱(chēng)之為懶漢式

示例代碼:

public class Singleton {  
  
    private static Singleton instance;  
  
    private Singleton () {}  
  
    public static Singleton getInstance() {  
      if (instance == null) {  
          instance = new Singleton();  
      }  
      return instance;  
    }  
}

分析懶漢式(線程不安全)寫(xiě)法的特點(diǎn):

  • 創(chuàng)建對(duì)象的時(shí)機(jī)修改為了在getInstance內(nèi)部,需要時(shí)再創(chuàng)建,這可以節(jié)約系統(tǒng)資源
  • getInstance方法在多個(gè)線程并發(fā)調(diào)用時(shí),有可能會(huì)出現(xiàn)創(chuàng)建了多個(gè)實(shí)例,所以這算是一個(gè)不好的單例變種示范

餓漢式?jīng)]有多線程并發(fā)問(wèn)題嗎?

確實(shí)沒(méi)有,因?yàn)轲I漢式是在類(lèi)加載時(shí)進(jìn)行創(chuàng)建對(duì)象,類(lèi)加載classloader是單線程的,不存在這個(gè)問(wèn)題。

3. 懶漢式(線程安全)

此變種僅是介紹,不要使用。

懶漢式(線程不安全)有可能存在并發(fā)問(wèn)題,導(dǎo)致創(chuàng)建多個(gè)實(shí)例,那么我們給他加上鎖不就好了嗎?

示例代碼:

public class Singleton {  
  
    private static Singleton instance;  
  
    private Singleton () {}  
  
    public static synchronized Singleton getInstance() {  
	    if (instance == null) {  
        instance = new Singleton();  
    	}  
  	  return instance;  
    }  
}

分析懶漢式寫(xiě)法的特點(diǎn):

由于調(diào)用getInstance時(shí)如果instance為null會(huì)創(chuàng)建對(duì)象,如果多個(gè)線程同時(shí)調(diào)用getInstance方法,有可能出現(xiàn)同步問(wèn)題導(dǎo)致創(chuàng)建多個(gè)實(shí)例,所以getInstance方法使用了synchronized加鎖來(lái)保障并發(fā)情況下也只會(huì)創(chuàng)建一個(gè)實(shí)例,不過(guò)synchronized的粒度較大,如果每次請(qǐng)求都經(jīng)過(guò)getInstance方法,性能影響較大。

4. 雙檢鎖/雙重校驗(yàn)鎖(DCL,double-checked locking)

懶漢式(線程安全)已經(jīng)可以達(dá)到節(jié)省資源的目的,也達(dá)到了線程安全的目的,但是使用synchronized加鎖對(duì)性能有較大影響,雙檢鎖的方式,則是把鎖的粒度盡可能降低,減少加鎖對(duì)性能的影響。

示例代碼:

public class Singleton {  
  
    private volatile static Singleton instance;  
  
    private Singleton () {}  
  
    public static Singleton getSingleton() {  
      if (instance == null) {  
          synchronized (Singleton.class) {  
            if (instance == null) {  
                instance = new Singleton();  
            }  
          }  
      }  
      return singleton;  
    }  
}

分析雙檢鎖的寫(xiě)法:

  • 在成員屬性instance上,我們?cè)黾恿藇olatile關(guān)鍵字,保障多線程對(duì)instance值的可見(jiàn)性以及禁止指令重排。
  • 通過(guò)雙重檢查的方式,在內(nèi)部再進(jìn)行synchronized加鎖,可以降低鎖的粒度,有效避免每次調(diào)用getInstance都加鎖,因?yàn)間etInstance在創(chuàng)建對(duì)象之后,instance一直都是非null的。

雙檢鎖這個(gè)方式,既可以保障不浪費(fèi)資源,又可以保障在多線程的環(huán)境下保持高性能。

如果大家自行編寫(xiě)單例類(lèi),追求節(jié)約資源和高性能,可以使用這種寫(xiě)法,但據(jù)《Java并發(fā)編程實(shí)踐》提到不贊成這個(gè)寫(xiě)法,推薦靜態(tài)內(nèi)部類(lèi)的方式(這一點(diǎn)我尚未驗(yàn)證)。

5. 靜態(tài)內(nèi)部類(lèi)

這個(gè)變種,可以達(dá)到和雙檢鎖一樣的效果,并且寫(xiě)起來(lái)更加簡(jiǎn)單,推薦使用。

public class Singleton {  
  
    private static class SingletonHolder {  
  	  private static final Singleton INSTANCE = new Singleton();  
    }  
  
    private Singleton () {}  
  
    public static final Singleton getInstance() {  
  	  return SingletonHolder.INSTANCE;  
    }  
}

分析一下靜態(tài)內(nèi)部類(lèi)的特點(diǎn):

將instance放在了內(nèi)部類(lèi)SingletonHolder中,前面我們提到餓漢式是類(lèi)加載時(shí)就會(huì)立即創(chuàng)建對(duì)象,而靜態(tài)內(nèi)部類(lèi)不會(huì),它只會(huì)在調(diào)用了getInstance時(shí),才會(huì)加載內(nèi)部類(lèi)SingletonHolder,此時(shí)才會(huì)創(chuàng)建對(duì)象。

6. 枚舉

這個(gè)方式,這里僅是從網(wǎng)上摘抄,據(jù)說(shuō)是很好,但是沒(méi)有試過(guò),工作中也很少見(jiàn)。

這種實(shí)現(xiàn)方式還沒(méi)有被廣泛采用,但這是實(shí)現(xiàn)單例模式的最佳方法。

它更簡(jiǎn)潔,自動(dòng)支持序列化機(jī)制,絕對(duì)防止多次實(shí)例化。

這種方式是 Effective Java 作者 Josh Bloch 提倡的方式,它不僅能避免多線程同步問(wèn)題,而且還自動(dòng)支持序列化機(jī)制,防止反序列化重新創(chuàng)建新的對(duì)象,絕對(duì)防止多次實(shí)例化。

不過(guò),由于 JDK1.5 之后才加入 enum 特性,用這種方式寫(xiě)不免讓人感覺(jué)生疏,在實(shí)際工作中,也很少用。

不能通過(guò) reflection attack 來(lái)調(diào)用私有構(gòu)造方法。

public enum Singleton {  
    INSTANCE;  
    public void whateverMethod() {  
    }  
}

7. 登記式

如果熟悉我們封裝的工具包Toolbox,就會(huì)知道工具包內(nèi)提供了一個(gè)登記式單例工具類(lèi)Singleton。

單例模式是一種非常常用的設(shè)計(jì)模式,但以上介紹的各種方法,都需要為每個(gè)單例類(lèi)編寫(xiě)一些模板式的代碼,為了簡(jiǎn)化,我們可以使用Singleton工具類(lèi)。

//    獲取單例對(duì)象
//    Student類(lèi)必須要具備無(wú)參構(gòu)造方法
//    每個(gè)類(lèi)在一個(gè)進(jìn)程中只能獲得一個(gè)單例對(duì)象
Student student = Singleton.get(Student.class);

//    移除單例對(duì)象
Singleton.remove(Student.class);

//    清空所有單例對(duì)象
Singleton.clear();

//    單例對(duì)象數(shù)量
int size = Singleton.size();

其實(shí)他就是很像是spring容器。

Singleton.java:

/**
 * 單例工具
 * @author Unicorn
 */
public final class Singleton {

    /**
     * 對(duì)象池
     */
    private static Map<String, Object> pool = new ConcurrentHashMap();

    private Singleton() {}

    public static <T> T get(Class<T> clazz) {
        Assert.notNull(clazz);
        String key = clazz.getName();
        T obj = (T) pool.get(key);
        if (null == obj) {
            synchronized(Singleton.class) {
                obj = (T) pool.get(key);
                if (null == obj) {
                    obj = ReflectUtil.newInstance(clazz);
                    pool.put(key, obj);
                }
            }
        }
        return obj;
    }

    /**
     * 移除對(duì)象
     * @param clazz
     */
    public static void remove(Class clazz) {
        if (null != clazz) {
            String key = clazz.getName();
            pool.remove(key);
        }
    }

    /**
     * 銷(xiāo)毀,清空對(duì)象池
     */
    public static void clear() {
        pool.clear();
    }

    public static int size() {
        return pool.size();
    }
}

8. Spring容器

spring容器核心機(jī)制是IoC和DI,其本身也提供了單例對(duì)象的支持。

到此這篇關(guān)于一文帶你搞懂Java單例模式的文章就介紹到這了,更多相關(guān)Java單例模式內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • java中的interface接口實(shí)例詳解

    java中的interface接口實(shí)例詳解

    這篇文章主要介紹了 java中的interface接口實(shí)例詳解的相關(guān)資料,需要的朋友可以參考下
    2017-03-03
  • Java中啟動(dòng)線程start和run的兩種方法

    Java中啟動(dòng)線程start和run的兩種方法

    start()方法它的作用是啟動(dòng)一個(gè)新線程,run()就和普通的成員方法一樣,可以被重復(fù)調(diào)用。接下來(lái)通過(guò)本文給大家分享Java中啟動(dòng)線程start和run的兩種方法,需要的朋友參考下吧
    2017-11-11
  • 基于Java ActiveMQ的實(shí)例講解

    基于Java ActiveMQ的實(shí)例講解

    下面小編就為大家?guī)?lái)一篇基于Java ActiveMQ的實(shí)例講解。小編覺(jué)得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧
    2017-09-09
  • JavaWeb實(shí)現(xiàn)Session跨頁(yè)面?zhèn)鬟f數(shù)據(jù)

    JavaWeb實(shí)現(xiàn)Session跨頁(yè)面?zhèn)鬟f數(shù)據(jù)

    本文主要介紹了 JavaWeb實(shí)現(xiàn)Session跨頁(yè)面?zhèn)鬟f數(shù)據(jù),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2023-07-07
  • SWT(JFace)體驗(yàn)之圖片的動(dòng)態(tài)漸變效果

    SWT(JFace)體驗(yàn)之圖片的動(dòng)態(tài)漸變效果

    SWT(JFace)體驗(yàn)之圖片的動(dòng)態(tài)漸變效果
    2009-06-06
  • java計(jì)算日期相差天數(shù)的4種簡(jiǎn)單方法舉例

    java計(jì)算日期相差天數(shù)的4種簡(jiǎn)單方法舉例

    最近在工作中遇見(jiàn)一個(gè)小需求,要求計(jì)算兩個(gè)日期之間相差幾天,下面這篇文章主要給大家介紹了關(guān)于java計(jì)算日期相差天數(shù)的4種簡(jiǎn)單方法,文中通過(guò)代碼介紹的非常詳細(xì),需要的朋友可以參考下
    2024-06-06
  • Springboot項(xiàng)目實(shí)現(xiàn)Mysql多數(shù)據(jù)源切換的完整實(shí)例

    Springboot項(xiàng)目實(shí)現(xiàn)Mysql多數(shù)據(jù)源切換的完整實(shí)例

    這篇文章主要給大家介紹了關(guān)于Springboot項(xiàng)目實(shí)現(xiàn)Mysql多數(shù)據(jù)源切換的完整實(shí)例,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2020-11-11
  • Spring @Component自定義注解實(shí)現(xiàn)詳解

    Spring @Component自定義注解實(shí)現(xiàn)詳解

    @Component是一個(gè)元注解,意思是可以注解其他類(lèi)注解,如@Controller @Service @Repository @Aspect。官方的原話是:帶此注解的類(lèi)看為組件,當(dāng)使用基于注解的配置和類(lèi)路徑掃描的時(shí)候,這些類(lèi)就會(huì)被實(shí)例化
    2022-09-09
  • JAVA實(shí)戰(zhàn)項(xiàng)目實(shí)現(xiàn)客戶選購(gòu)系統(tǒng)詳細(xì)流程

    JAVA實(shí)戰(zhàn)項(xiàng)目實(shí)現(xiàn)客戶選購(gòu)系統(tǒng)詳細(xì)流程

    讀萬(wàn)卷書(shū)不如行萬(wàn)里路,只學(xué)書(shū)上的理論是遠(yuǎn)遠(yuǎn)不夠的,只有在實(shí)戰(zhàn)中才能獲得能力的提升,本篇文章手把手帶你用Java實(shí)現(xiàn)一個(gè)簡(jiǎn)單的客戶選購(gòu)系統(tǒng),大家可以在過(guò)程中查缺補(bǔ)漏,提升水平
    2021-10-10
  • eclipse創(chuàng)建springboot項(xiàng)目的三種方式總結(jié)

    eclipse創(chuàng)建springboot項(xiàng)目的三種方式總結(jié)

    這篇文章主要介紹了eclipse創(chuàng)建springboot項(xiàng)目的三種方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2021-07-07

最新評(píng)論