最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

淺談Java編程中的單例設(shè)計(jì)模式

 更新時(shí)間:2015年07月31日 15:28:39   作者:zinss26914  
這篇文章主要介紹了Java編程中的單例設(shè)計(jì)模式,在許多語(yǔ)言的編程過(guò)程當(dāng)中單例模式都被開發(fā)者們廣泛采用,需要的朋友可以參考下

寫軟件的時(shí)候經(jīng)常需要用到打印日志功能,可以幫助你調(diào)試和定位問(wèn)題,項(xiàng)目上線后還可以幫助你分析數(shù)據(jù)。但是Java原生帶有的System.out.println()方法卻很少在真正的項(xiàng)目開發(fā)中使用,甚至像findbugs等代碼檢查工具還會(huì)認(rèn)為使用System.out.println()是一個(gè)bug。

為什么作為Java新手神器的System.out.println(),到了真正項(xiàng)目開發(fā)當(dāng)中會(huì)被唾棄呢?其實(shí)只要細(xì)細(xì)分析,你就會(huì)發(fā)現(xiàn)它的很多弊端。比如不可控制,所有的日志都會(huì)在項(xiàng)目上線后照常打印,從而降低運(yùn)行效率;又或者不能將日志記錄到本地文件,一旦打印被清除,日志將再也找不回來(lái);再或者打印的內(nèi)容沒有Tag區(qū)分,你將很難辨別這一行日志是在哪個(gè)類里打印的。

你的leader也不是傻瓜,用System.out.println()的各項(xiàng)弊端他也清清楚楚,因此他今天給你的任務(wù)就是制作一個(gè)日志工具類,來(lái)提供更好的日志功能。不過(guò)你的leader人還不錯(cuò),并沒讓你一開始就實(shí)現(xiàn)一個(gè)具備各項(xiàng)功能的牛逼日志工具類,只需要一個(gè)能夠控制打印級(jí)別的日志工具就好。

這個(gè)需求對(duì)你來(lái)說(shuō)并不難,你立刻就開始動(dòng)手編寫了,并很快完成了第一個(gè)版本:

  public class LogUtil { 
    public final int DEBUG = 0; 
   
    public final int INFO = 1; 
   
    public final int ERROR = 2; 
   
    public final int NOTHING = 3; 
   
    public int level = DEBUG; 
   
    public void debug(String msg) { 
      if (DEBUG >= level) { 
        System.out.println(msg); 
      } 
    } 
   
    public void info(String msg) { 
      if (INFO >= level) { 
        System.out.println(msg); 
      } 
    } 
   
    public void error(String msg) { 
      if (ERROR >= level) { 
        System.out.println(msg); 
      } 
    } 
  } 



通過(guò)這個(gè)類來(lái)打印日志,只需要控制level的級(jí)別,就可以自由地控制打印的內(nèi)容。比如現(xiàn)在項(xiàng)目處于開發(fā)階段,就將level設(shè)置為DEBUG,這樣所有的日志信息都會(huì)被打印。而項(xiàng)目如果上線了,可以把level設(shè)置為INFO,這樣就只能看到INFO及以上級(jí)別的日志打印。如果你只想看到錯(cuò)誤日志,就可以把level設(shè)置為ERROR。而如果你開發(fā)的項(xiàng)目是客戶端版本,不想讓任何日志打印出來(lái),可以將level設(shè)置為NOTHING。打印的時(shí)候只需要調(diào)用:

  new LogUtil().debug("Hello World!"); 



你迫不及待地將這個(gè)工具介紹給你的leader,你的leader聽完你的介紹后說(shuō):“好樣的,今后大伙都用你寫的這個(gè)工具來(lái)打印日志了!”

可是沒過(guò)多久,你的leader找到你來(lái)反饋問(wèn)題了。他說(shuō)雖然這個(gè)工具好用,可是打印這種事情是不區(qū)分對(duì)象的,這里每次需要打印日志的時(shí)候都需要new出一個(gè)新的LogUtil,太占用內(nèi)存了,希望你可以將這個(gè)工具改成用單例模式實(shí)現(xiàn)。

你認(rèn)為你的leader說(shuō)的很有道理,而且你也正想趁這個(gè)機(jī)會(huì)練習(xí)使用一下設(shè)計(jì)模式,于是你寫出了如下的代碼(ps:這里代碼是我自己實(shí)現(xiàn)的,而且我開始確實(shí)沒注意線程同步問(wèn)題):

  public class LogUtil { 
    private static LogUtil logUtilInstance; 
   
    public final int DEBUG = 0; 
   
    public final int INFO = 1; 
   
    public final int ERROR = 2; 
   
    public final int NOTHING = 3; 
   
    public int level = DEBUG; 
   
    private LogUtil() { 
   
    } 
   
    public static LogUtil getInstance() { 
      if (logUtilInstance == null) { 
        logUtilInstance = new LogUtil(); 
      } 
   
      return logUtilInstance; 
    } 
   
    public void debug(String msg) { 
      if (DEBUG >= level) { 
        System.out.println(msg); 
      } 
    } 
   
    public void info(String msg) { 
      if (INFO >= level) { 
        System.out.println(msg); 
      } 
    } 
   
    public void error(String msg) { 
      if (ERROR >= level) { 
        System.out.println(msg); 
      } 
    } 
   
    public static void main(String[] args) { 
      LogUtil.getInstance().debug("Hello World!"); 
    } 
  } 


首先將LogUtil的構(gòu)造函數(shù)私有化,這樣就無(wú)法使用new關(guān)鍵字來(lái)創(chuàng)建LogUtil的實(shí)例了。然后使用一個(gè)sLogUtil私有靜態(tài)變量來(lái)保存實(shí)例,并提供一個(gè)公有的getInstance方法用于獲取LogUtil的實(shí)例,在這個(gè)方法里面判斷如果sLogUtil為空,就new出一個(gè)新的LogUtil實(shí)例,否則就直接返回sLogUtil。這樣就可以保證內(nèi)存當(dāng)中只會(huì)存在一個(gè)LogUtil的實(shí)例了。單例模式完工!這時(shí)打印日志的代碼需要改成如下方式:

  LogUtil.getInstance().debug("Hello World");  


你將這個(gè)版本展示給你的leader瞧,他看后笑了笑,說(shuō):“雖然這看似是實(shí)現(xiàn)了單例模式,可是還存在著bug的哦。
你滿腹狐疑,單例模式不都是這樣實(shí)現(xiàn)的嗎?還會(huì)有什么bug呢?

你的leader提示你,使用單例模式就是為了讓這個(gè)類在內(nèi)存中只能有一個(gè)實(shí)例的,可是你有考慮到在多線程中打印日志的情況嗎?如下面代碼所示:

  public static LogUtil getInstance() { 
    if (logUtilInstance == null) { 
      logUtilInstance = new LogUtil(); 
    } 
   
    return logUtilInstance; 
  } 


如果現(xiàn)在有兩個(gè)線程同時(shí)在執(zhí)行g(shù)etInstance方法,第一個(gè)線程剛執(zhí)行完第2行,還沒執(zhí)行第3行,這個(gè)時(shí)候第二個(gè)線程執(zhí)行到了第2行,它會(huì)發(fā)現(xiàn)sLogUtil還是null,于是進(jìn)入到了if判斷里面。這樣你的單例模式就失敗了,因?yàn)閯?chuàng)建了兩個(gè)不同的實(shí)例。
你恍然大悟,不過(guò)你的思維非常快,立刻就想到了解決辦法,只需要給方法加上同步鎖就可以了,代碼如下:

  public synchronized static LogUtil getInstance() { 
    if (logUtilInstance == null) { 
      logUtilInstance = new LogUtil(); 
    } 
   
    return logUtilInstance; 
  } 


這樣,同一時(shí)刻只允許有一個(gè)線程在執(zhí)行g(shù)etInstance里面的代碼,這樣就有效地解決了上面會(huì)創(chuàng)建兩個(gè)實(shí)例的情況。
你的leader看了你的新代碼后說(shuō):“恩,不錯(cuò)。這確實(shí)解決了有可能創(chuàng)建兩個(gè)實(shí)例的情況,但是這段代碼還是有問(wèn)題的?!?/p>

你緊張了起來(lái),怎么還會(huì)有問(wèn)題???

你的leader笑笑:“不用緊張,這次不是bug,只是性能上可以優(yōu)化一些。你看一下,如果是在getInstance方法上加了一個(gè)synchronized,那么我每次去執(zhí)行g(shù)etInstace方法的時(shí)候都會(huì)受到同步鎖的影響,這樣運(yùn)行的效率會(huì)降低,其實(shí)只需要在第一次創(chuàng)建LogUtil實(shí)例的時(shí)候加上同步鎖就好了。我來(lái)教你一下怎么把它優(yōu)化的更好?!?/p>

首先將synchronized關(guān)鍵字從方法聲明中去除,把它加入到方法體當(dāng)中:

  public static LogUtil getInstance() { 
    if (logUtilInstance == null) { 
      synchronized (LogUtil.class) { 
        if (logUtilInstance == null) { 
          // 這里是必須的,因?yàn)橛锌赡軆蓚€(gè)進(jìn)程同時(shí)執(zhí)行到synchronized之前 
          logUtilInstance = new LogUtil(); 
        } 
      } 
    } 
   
    return logUtilInstance; 
  } 


代碼改成這樣之后,只有在sLogUtil還沒被初始化的時(shí)候才會(huì)進(jìn)入到第3行,然后加上同步鎖。等sLogUtil一但初始化完成了,就再也走不到第3行了,這樣執(zhí)行g(shù)etInstance方法也不會(huì)再受到同步鎖的影響,效率上會(huì)有一定的提升。
你情不自禁贊嘆到,這方法真巧妙啊,能想得出來(lái)實(shí)在是太聰明了。

你的leader馬上謙虛起來(lái):“這種方法叫做雙重鎖定(Double-Check Locking),可不是我想出來(lái)的,更多的資料你可以在網(wǎng)上查一查?!?/p>

其實(shí)在java里實(shí)現(xiàn)單例我更習(xí)慣用餓漢模式

懶漢式的特點(diǎn)是延遲加載,實(shí)例直到用到的時(shí)候才會(huì)加載

餓漢式的特點(diǎn)是一開始就加載了,所以每次用到的時(shí)候直接返回即可(我更推薦這一種,因?yàn)椴恍枰紤]太多線程安全的問(wèn)題,當(dāng)然懶漢式是可以通過(guò)上文所說(shuō)的雙重鎖定解決同步問(wèn)題的)

用餓漢式實(shí)現(xiàn)日志記錄的代碼如下:

  public class LogUtil { 
    private static final LogUtil logUtilInstance = new LogUtil(); 
   
    public final int DEBUG = 0; 
   
    public final int INFO = 1; 
   
    public final int ERROR = 2; 
   
    public final int NOTHING = 3; 
   
    public int level = DEBUG; 
   
    private LogUtil() { 
   
    } 
   
    public static LogUtil getInstance() { 
      return logUtilInstance; 
    } 
   
    public void debug(String msg) { 
      if (DEBUG >= level) { 
        System.out.println(msg); 
      } 
    } 
   
    public void info(String msg) { 
      if (INFO >= level) { 
        System.out.println(msg); 
      } 
    } 
   
    public void error(String msg) { 
      if (ERROR >= level) { 
        System.out.println(msg); 
      } 
    } 
   
    public static void main(String[] args) { 
      logUtil.getInstance().debug("Hello World!"); 
    } 
  } 


相關(guān)文章

  • SpringBoot  jdbctemplate使用方法解析

    SpringBoot jdbctemplate使用方法解析

    這篇文章主要介紹了SpringBoot jdbctemplate使用方法解析,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2020-05-05
  • Spring:@Async注解和AsyncResult與CompletableFuture使用問(wèn)題

    Spring:@Async注解和AsyncResult與CompletableFuture使用問(wèn)題

    這篇文章主要介紹了Spring:@Async注解和AsyncResult與CompletableFuture使用問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2024-08-08
  • SpringBoot消息國(guó)際化配置實(shí)現(xiàn)過(guò)程解析

    SpringBoot消息國(guó)際化配置實(shí)現(xiàn)過(guò)程解析

    這篇文章主要介紹了SpringBoot消息國(guó)際化配置實(shí)現(xiàn)過(guò)程解析,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2020-07-07
  • Java基礎(chǔ)知識(shí)總結(jié)之繼承

    Java基礎(chǔ)知識(shí)總結(jié)之繼承

    這一篇我們來(lái)學(xué)習(xí)面向?qū)ο蟮牡诙€(gè)特征——繼承,文中有非常詳細(xì)的基礎(chǔ)知識(shí)總結(jié),對(duì)正在學(xué)習(xí)java的小伙伴們很有幫助,需要的朋友可以參考下
    2021-06-06
  • 淺談Java解釋器模式

    淺談Java解釋器模式

    這篇文章主要介紹了Java基于解釋器模式實(shí)現(xiàn)定義一種簡(jiǎn)單的語(yǔ)言功能,簡(jiǎn)單描述了解釋器模式的概念、功能及Java使用解釋器模式定義一種簡(jiǎn)單語(yǔ)言的相關(guān)實(shí)現(xiàn)與使用技巧,需要的朋友可以參考下
    2021-10-10
  • java如何接收和發(fā)送ASCII數(shù)據(jù)

    java如何接收和發(fā)送ASCII數(shù)據(jù)

    這篇文章主要介紹了java如何接收和發(fā)送ASCII數(shù)據(jù)問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2023-09-09
  • Java正則表達(dá)式之全量匹配和部分匹配

    Java正則表達(dá)式之全量匹配和部分匹配

    正則表達(dá)式異常強(qiáng)大,一直理解不深,用的也不深,這次項(xiàng)目中嘗試,體會(huì)到了它的強(qiáng)大之處,這篇文章主要給大家介紹了關(guān)于Java正則表達(dá)式之全量匹配和部分匹配的相關(guān)資料,需要的朋友可以參考下
    2023-06-06
  • Java實(shí)現(xiàn)簡(jiǎn)單QQ聊天工具

    Java實(shí)現(xiàn)簡(jiǎn)單QQ聊天工具

    這篇文章主要為大家詳細(xì)介紹了Java實(shí)現(xiàn)簡(jiǎn)單QQ聊天工具,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2022-09-09
  • Java中BigDecimal類與int、Integer使用總結(jié)

    Java中BigDecimal類與int、Integer使用總結(jié)

    這篇文章主要給大家介紹了關(guān)于Java中BigDecimal類與int、Integer使用的相關(guān)資料,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家學(xué)習(xí)或者使用Java具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2019-07-07
  • Spring中ClassPath指的是哪些地方

    Spring中ClassPath指的是哪些地方

    在Spring應(yīng)用中,ClassPath指的是應(yīng)用程序的類加載路徑,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2024-06-06

最新評(píng)論

尖扎县| 喀喇沁旗| 尉犁县| 东海县| 勃利县| 太保市| 美姑县| 金华市| 中西区| 鄂托克前旗| 叙永县| 阆中市| 彭州市| 张家口市| 蒲城县| 方城县| 福鼎市| 吉安市| 昌邑市| 松阳县| 霍林郭勒市| 岑巩县| 玉林市| 南京市| 乃东县| 中江县| 德安县| 元江| 韶山市| 长寿区| 鸡泽县| 山东| 西平县| 宣汉县| 大姚县| 孝义市| 瓦房店市| 宜黄县| 绥滨县| 莱阳市| 仁化县|