請注意這個機制只有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 行號每人都會不一樣)
執行流程如下:
- doIt()
- throw new WrapperException("doIt...")
- first catch(Exception ex) block
- finally block
- close()
- 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中的行號每人不同)
上述流程為:
- doIt()
- throw new WrapperException("doIt...")
- immediately call close()
- close()
- Suppressed:throw new WrapperException("close...")
- 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())的順序為反向的:
- bufferedInput.close()
- 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
話說好便宜阿作者真是佛心來者...
沒有留言:
張貼留言