2013年7月20日 星期六

Java IO: Exception Handling - 不安全的IO Exception處理(2) - 使用try-with-resources

參考:Try-with-resources in Java 7  、 try-with-resources

請注意這個機制只有java 7後才適用

上篇,我們這次來看看java7的新機制(同樣是參考文章後的心得形式,若需詳細請直接連入上方參考文章連結)。


先假設下面的code:
 

public class TryWithResource implements AutoCloseable {

 private FileInputStream _input;

 public void doIt() throws WrapperException, FileNotFoundException {
  _input = new FileInputStream("datasheet/file.txt");
  throw new WrapperException("doIt has an Exception!!!");
 }

 @Override
 public void close() throws IOException, WrapperException {
  _input.close();
  throw new WrapperException("close has an Exception!!!");
 }
 }

以下列方式呼叫使用:
 
 public static void withoutTryWithResources() throws WrapperException {
  TryWithResource tt = new TryWithResource();
  try {
   tt.doIt();
  } catch (Exception ex) {
   throw new WrapperException(ex);
  } finally {
   try {
    tt.close();
   } catch (IOException e) {
    throw new WrapperException(e);
   }
  }
 }
 public static void main(String[] args) {
  try {
   withoutTryWithResources();
  } catch (WrapperException wex) {
   wex.printStackTrace();
  }
 }

會有以下結果:
Exception in thread "main" java.lang.IndexOutOfBoundsException
exception.WrapperException: close has an Exception!!!
 at exception.failsave.TryWithResource.close(TryWithResource.java:12)
 at exception.failsave.TryWithResource.withoutTryWithResources(TryWithResource.java:32)
 at exception.failsave.TryWithResource.main(TryWithResource.java:49)

(上述的exception 行號每人都會不一樣)
執行流程如下:

  1. doIt()
  2. throw new WrapperException("doIt...")
  3. first catch(Exception ex) block
  4. finally block
  5. close()
  6. throw new WrapperException("close...")

最始祖的exception為應該為doIt()中出現的,但如同前一篇所說,始祖exception會被吃掉,因此,到了步驟6原先的始祖exception(步驟2)就消失了,這造成我們難以在第一時間發現最根本的問題點,因此我們可以使用java7的新機制,修改如下:

 
 public static void withResources() throws WrapperException {
  try (TryWithResource tt = new TryWithResource()) {
   tt.doIt();
  } catch (Exception e) {
   throw new WrapperException(e);
  }
 }

 public static void main(String[] args) {
  try {
//   withoutTryWithResources();
   withResources();
  } catch (WrapperException wex) {
   wex.printStackTrace();
  }
 }

而這次的exception 顯示為:
exception.WrapperException: exception.WrapperException: doIt has an Exception!!!
 at exception.failsave.TryWithResource.withResources(TryWithResource.java:43)
 at exception.failsave.TryWithResource.main(TryWithResource.java:50)
Caused by: exception.WrapperException: doIt has an Exception!!!
 at exception.failsave.TryWithResource.doIt(TryWithResource.java:15)
 at exception.failsave.TryWithResource.withResources(TryWithResource.java:41)
 ... 1 more
 Suppressed: exception.WrapperException: close has an Exception!!!
  at exception.failsave.TryWithResource.close(TryWithResource.java:21)
  at exception.failsave.TryWithResource.withResources(TryWithResource.java:42)
  ... 1 more

(再一次提醒,exception中的行號每人不同)
上述流程為:
  1. doIt()
  2. throw new WrapperException("doIt...")
  3. immediately call close() 
  4. close()
  5. Suppressed:throw new WrapperException("close...")  
  6. throw WrapperException("doIt...") (step 2)

不僅少了很多恐怖的try catch跟finally還很清楚地馬上就知道始祖exception問題處。
注意其中的Suppressed: exception.WrapperException: close has an Exception!!!(步驟5)
由於使用了try-with-resources機制,因此後面發生在close()中的exception就會被suppress(壓制)。

而try-with-resources機制也可以同時使用多個:
 
private static void printFileJava7() throws IOException {

    try(  FileInputStream     input         = new FileInputStream("file.txt");
          BufferedInputStream bufferedInput = new BufferedInputStream(input)
    ) {

        int data = bufferedInput.read();
        while(data != -1){
            System.out.print((char) data);
    data = bufferedInput.read();
        }
    }
}

如果發生了問題,則destructor (就是呼叫close())的順序為反向的:

  1. bufferedInput.close()
  2. input.close()

另外還有一個限制,使用在try-with-resources都必須在try(...)中宣告,而不能使用下列方式:
 
private static void printFileJava7() throws IOException {
   FileInputStream     input = null;
   BufferedInputStream bufferedInput = null;
    try(  input         = new FileInputStream("file.txt");
          bufferedInput = new BufferedInputStream(input)
    ) {

        int data = bufferedInput.read();
        while(data != -1){
            System.out.print((char) data);
    data = bufferedInput.read();
        }
    }
}


因此只要implement AutoCloseable 這個interface,除了原生java的class以外,自己實作的class也同樣可以享受try-with-resources 的好處呢!

以後解決這種惱人的io問題總算有解了!!exception handling真是個很重要的課題阿!!該去買文章作者的kindle電子書了Java Exception Handling
話說好便宜阿作者真是佛心來者...

沒有留言:

張貼留言