一区二区三区在线-一区二区三区亚洲视频-一区二区三区亚洲-一区二区三区午夜-一区二区三区四区在线视频-一区二区三区四区在线免费观看

服務器之家:專注于服務器技術及軟件下載分享
分類導航

PHP教程|ASP.NET教程|JAVA教程|ASP教程|

服務器之家 - 編程語言 - JAVA教程 - 淺談Java編程中的單例設計模式

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

2019-12-31 14:46zinss26914 JAVA教程

這篇文章主要介紹了Java編程中的單例設計模式,在許多語言的編程過程當中單例模式都被開發者們廣泛采用,需要的朋友可以參考下

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

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

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

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

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
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的級別,就可以自由地控制打印的內容。比如現在項目處于開發階段,就將level設置為DEBUG,這樣所有的日志信息都會被打印。而項目如果上線了,可以把level設置為INFO,這樣就只能看到INFO及以上級別的日志打印。如果你只想看到錯誤日志,就可以把level設置為ERROR。而如果你開發的項目是客戶端版本,不想讓任何日志打印出來,可以將level設置為NOTHING。打印的時候只需要調用:

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

 


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

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

你認為你的leader說的很有道理,而且你也正想趁這個機會練習使用一下設計模式,于是你寫出了如下的代碼(ps:這里代碼是我自己實現的,而且我開始確實沒注意線程同步問題):

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
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的構造函數私有化,這樣就無法使用new關鍵字來創建LogUtil的實例了。然后使用一個sLogUtil私有靜態變量來保存實例,并提供一個公有的getInstance方法用于獲取LogUtil的實例,在這個方法里面判斷如果sLogUtil為空,就new出一個新的LogUtil實例,否則就直接返回sLogUtil。這樣就可以保證內存當中只會存在一個LogUtil的實例了。單例模式完工!這時打印日志的代碼需要改成如下方式:

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


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

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

?
1
2
3
4
5
6
7
public static LogUtil getInstance() {
  if (logUtilInstance == null) {
    logUtilInstance = new LogUtil();
  }
 
  return logUtilInstance;
}


如果現在有兩個線程同時在執行getInstance方法,第一個線程剛執行完第2行,還沒執行第3行,這個時候第二個線程執行到了第2行,它會發現sLogUtil還是null,于是進入到了if判斷里面。這樣你的單例模式就失敗了,因為創建了兩個不同的實例。
你恍然大悟,不過你的思維非常快,立刻就想到了解決辦法,只需要給方法加上同步鎖就可以了,代碼如下:

?
1
2
3
4
5
6
7
public synchronized static LogUtil getInstance() {
  if (logUtilInstance == null) {
    logUtilInstance = new LogUtil();
  }
 
  return logUtilInstance;
}


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

你緊張了起來,怎么還會有問題啊?

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

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

?
1
2
3
4
5
6
7
8
9
10
11
12
public static LogUtil getInstance() {
  if (logUtilInstance == null) {
    synchronized (LogUtil.class) {
      if (logUtilInstance == null) {
        // 這里是必須的,因為有可能兩個進程同時執行到synchronized之前
        logUtilInstance = new LogUtil();
      }
    }
  }
 
  return logUtilInstance;
}


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

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

 

其實在java里實現單例我更習慣用餓漢模式

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

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

用餓漢式實現日志記錄的代碼如下:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
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!");
  }
}

延伸 · 閱讀

精彩推薦
主站蜘蛛池模板: 扒开女人下面使劲桶屁股动漫 | chinesefree普通对话 | 久久亚洲午夜牛牛影视 | 日韩综合一区 | 大妹子最新视频在线观看 | 国产精品久久久久一区二区三区 | 美女班主任下面好爽好湿好紧 | 好湿好滑好硬好爽好深视频 | 欧美怡红院视频一区二区三区 | 2019男人天堂 | 四虎最新免费观看网址 | 日韩免费视频播播 | 91视频免费观看网站 | 校花在公车上被内射好舒服 | 农村妇女野战bbxxx农村妇女 | 视频免费看 | 亚洲第一福利网 | 国产香蕉国产精品偷在线观看 | 亚州性夜夜射在线观看 | 日韩在线观看一区二区不卡视频 | 日韩亚洲一区中文字幕在线 | 全日爱韩国视频在线观看 | 欧美黑人一级 | 特黄aa级毛片免费视频播放 | 视频一区久久 | 午夜精品久久久久久久99蜜桃 | 国产自拍啪啪 | 我的妹妹最近有点怪免费播放 | 视频网站入口在线看 | 亚洲欧美成人综合久久久 | 海角社区在线视频 | 国产美女久久久久 | se在线播放 | 国产欧美视频在线观看 | 大学生宿舍飞机china free | 性欧美高清理论片 | 欧美男女交配 | 青青成人在线 | 欧亚专线欧洲m码可遇不可求 | 国产精品久久久久影院色老大 | 天美视频在线 |