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

淺談Java編程中的單例設計模式

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

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

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

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

這個需求對你來說并不難,你立刻就開始動手編寫了,并很快完成了第一個版本:

  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); 
      } 
    } 
  } 



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

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



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

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

你認為你的leader說的很有道理,而且你也正想趁這個機會練習使用一下設計模式,于是你寫出了如下的代碼(ps:這里代碼是我自己實現(xià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的構造函數(shù)私有化,這樣就無法使用new關鍵字來創(chuàng)建LogUtil的實例了。然后使用一個sLogUtil私有靜態(tài)變量來保存實例,并提供一個公有的getInstance方法用于獲取LogUtil的實例,在這個方法里面判斷如果sLogUtil為空,就new出一個新的LogUtil實例,否則就直接返回sLogUtil。這樣就可以保證內存當中只會存在一個LogUtil的實例了。單例模式完工!這時打印日志的代碼需要改成如下方式:

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


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

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

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


如果現(xiàn)在有兩個線程同時在執(zhí)行getInstance方法,第一個線程剛執(zhí)行完第2行,還沒執(zhí)行第3行,這個時候第二個線程執(zhí)行到了第2行,它會發(fā)現(xiàn)sLogUtil還是null,于是進入到了if判斷里面。這樣你的單例模式就失敗了,因為創(chuàng)建了兩個不同的實例。
你恍然大悟,不過你的思維非??欤⒖叹拖氲搅私鉀Q辦法,只需要給方法加上同步鎖就可以了,代碼如下:

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


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

你緊張了起來,怎么還會有問題?。?/p>

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

首先將synchronized關鍵字從方法聲明中去除,把它加入到方法體當中:

  public static LogUtil getInstance() { 
    if (logUtilInstance == null) { 
      synchronized (LogUtil.class) { 
        if (logUtilInstance == null) { 
          // 這里是必須的,因為有可能兩個進程同時執(zhí)行到synchronized之前 
          logUtilInstance = new LogUtil(); 
        } 
      } 
    } 
   
    return logUtilInstance; 
  } 


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

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

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

懶漢式的特點是延遲加載,實例直到用到的時候才會加載

餓漢式的特點是一開始就加載了,所以每次用到的時候直接返回即可(我更推薦這一種,因為不需要考慮太多線程安全的問題,當然懶漢式是可以通過上文所說的雙重鎖定解決同步問題的)

用餓漢式實現(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!"); 
    } 
  } 


相關文章

  • SpringBoot  jdbctemplate使用方法解析

    SpringBoot jdbctemplate使用方法解析

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

    Spring:@Async注解和AsyncResult與CompletableFuture使用問題

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

    SpringBoot消息國際化配置實現(xiàn)過程解析

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

    Java基礎知識總結之繼承

    這一篇我們來學習面向對象的第二個特征——繼承,文中有非常詳細的基礎知識總結,對正在學習java的小伙伴們很有幫助,需要的朋友可以參考下
    2021-06-06
  • 淺談Java解釋器模式

    淺談Java解釋器模式

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

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

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

    Java正則表達式之全量匹配和部分匹配

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

    Java實現(xiàn)簡單QQ聊天工具

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

    Java中BigDecimal類與int、Integer使用總結

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

    Spring中ClassPath指的是哪些地方

    在Spring應用中,ClassPath指的是應用程序的類加載路徑,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2024-06-06

最新評論

方城县| 清徐县| 元谋县| 扶绥县| 林芝县| 邵阳市| 常山县| 隆林| 鱼台县| 延边| 神农架林区| 蚌埠市| 美姑县| 朝阳区| 鄂伦春自治旗| 临海市| 隆安县| 当涂县| 上杭县| 湘阴县| 左贡县| 仁寿县| 龙山县| 洞头县| 体育| 伊春市| 昭苏县| 沙湾县| 肇庆市| 祁门县| 闵行区| 德惠市| 曲沃县| 华坪县| 巴林左旗| 霍林郭勒市| 潞城市| 乌审旗| 肇庆市| 尼勒克县| 宝兴县|