Google Prettify

2014-11-07

Java 新功能讓你不怕忘記關閉資源─try-with-resource statement介紹

Resource的release 是在使用resource物件時,所特別需要注意的事,小則AP掛掉,大則機器損毀;所以凡是開發人員,在使用resource物件時,都要特別小心,像是ConnectionFileI/O Stream等。可是根據莫菲定律,越擔心會發生的事就越會發生,code寫久了,總有一天你會忘記release resource…

適合閱讀

本篇適合以下人員閱讀:

  • 知道Java不只是咖啡的人
  • 會寫Hello Java的人

好吧,如果你不會寫程式,也可以看看,提供一些意見;如果你是dot Net高手,也請不吝指教。

正文

開發人員在開發的時候,除了bug之外,最怕的就是忘記關閉一些已使用資源物件。 1.7 版本以後,所有的Closeable物件都繼承了AutoCloseable;也同時提供了一個新的語法:try-with-resoure statement。 今天主要介紹這個try-with-resource的用法。

我們一般的try-catch寫法是這樣的:

public void normalTry() throws Exception {
    String sql = "This is a sql statement with parameter";
    Connection conn = null;
    PreparedStatement ps = null;
    ResultSet rs = null;
    try {
        conn = getConnection();// Any methodology to get connection.
        ps = conn.prepareStatement(sql);
        ps.setString(1, "This is parameter");
        rs = ps.executeQuery();
        while (rs.next()) {
            rs.getString(1);
        }
    } finally {
        try {
            rs.close();
            ps.close();
            conn.close();
        } catch (Exception e) {
            throw e;
        }
    }
}

一般的try-catch-finally寫法,物件的宣告必需在try-catch之前,而且必需要有個finally來releaseCloseable的物件。1.7 以後你可以在try後面增加( )並在裡面放置一些需要close的物件。 這裡是一個try-with-resource的範例:

public void tryWithResource() throws Exception {
    String sql = "This is a sql statement";
    try (Connection conn = getConnection(); 
        PreparedStatement ps = conn.prepareStatement(sql)) {
        ps.setString(1, "This is parameter");
        try (ResultSet rs = ps.executeQuery(sql);) {
            while (rs.next()) {
                rs.getString(1);
            }
        }
    }
}

使用新的try-with-resource寫法,程式行數由23行減為10行。

Try-with-resource也可以像一般try-catch的方式,搭配catch與finally服用。 簡單來說try-with-resource statement,就是這樣。

不過事情並不是這麼單純。

進階

我們把進階分成優點跟缺點來看…

優點

  • Try-with-resource statement 可以防止開發人員因為忘記關閉resource導致系統 crash。

    還記得我們一開始提到AutoCloseable嗎? 在try 裡面的東西不是阿貓阿狗都可以放進來,他必需要是AutoCloseable的物件。原本1.6以前的Closeable物件(其實是Interface),現在已經直接繼承AutoCloseable,所以所有的Closeable物件都是AutoCloseable物件。 JVM會在離開try之後依建構的反向順序 呼叫AutoCloseable.close()

    PS: CloseableAutoCloseable都是interface

  • 可以減少程式碼。 不言自明,由範例來看,程式碼很明顯的減少了。

缺點

  • 程式碼可能不容易閱讀。

    tryWithResource的範例中,ResultSet必需在PreapreStatement設定參數後才能執行,才能建構,所以沒辦法放與ConnectionPrepareStatement放在同一個try()括號中,因此出現在巢狀的try敘述。 也許看久了會習慣,不過以現時的我來說,還沒那麼習慣。當然,這純屬個人偏好;但想像一下,這種情形如果重覆很多層,有時候,真的會很難搞清楚最後一個大括號,到底是對應那一個。

  • 當其他錯誤與close()同時發生錯誤的話,close()的錯誤會被蓋掉。

    考慮以下程式碼:

      public void suppressException() throws Exception {
          String sql = "This is a sql statement";
          try (Connection conn = getConnection(); PreparedStatement ps = conn.prepareStatement(sql)) {
              if (allwaysReturnTrue()) {
                  throw new OperationNotSupportedException();
              }
      }

    假設當Connectionclose()時發生錯誤,log只會看到OperationNotSupportedException()所拋出的錯誤,不會看到close()的錯誤。不過這有方法可以處理,就是使用 Throwable.getSuppressed()

      public void catchSuppressException() throws Exception {
          String sql = "This is a sql statement";
          try (Connection conn = getConnection(); PreparedStatement ps = conn.prepareStatement(sql)) {
              if (allwaysReturnTrue()) {
                  throw new OperationNotSupportedException();
              }
          }
      }
    
      public static void main(String[] args) {
          TryWithRessourceDemo d=new TryWithRessourceDemo();
          try {
              d.catchSuppressException();
          } catch (Throwable e) {
              System.out.println("main");
              System.out.println(e);
              Throwable[] suppressed = e.getSuppressed();//Use getSuppressed() to find suppressed exception
              for (int i = 0; i < suppressed.length; i++) {
                  Throwable throwable = suppressed[i];
                  System.out.println(throwable);
              }
          }
      }

小結

本篇介紹了try-with-resource的用法與特性,好用,但用得太過很容造成閱讀障礙。 雖然close()不太容易會發生錯誤,但要注意suppressed exception的問題

同場加映

一開始學Java時,只道try-catch必需成雙成對,最多是只有finally可以選擇性的加與不加; 有人知道catch與finally都可以選擇加與不加嗎?

//我以為try-catch只有2種寫法
try{...} catch(){...}
try{...} catch(){...} finally{...}
//偶然看到高手這麼用
try{...} finally{...}

沒有留言 :

您或許對這些有興趣

Related Posts with Thumbnails

最後

謝謝您的閱讀,希望您可以有豐富的收獲。