談談Java的try..catch...

咱們在寫Java的try..catch的時候,每每須要在最後加上finally子句關閉一些IO資源,好比java

?函數

1
2
3
4
5
6
7
8
9
10
11
12
InputStream is;
try {
     is=openInputStream();
     // do something
} catch (IOException e){
     e.printStaceTrace(e);
} finally {
     try {
         is.close();
     } catch (IOException e ){
     }
}
   

但 是在使用這種模式時,即便是Java老手,偶爾也會犯一些錯誤。好比上面這段代碼,當openInputStream()函數在執行過程當中拋出異常,那麼 變量is的值仍爲null,此時執行is.close()會拋出NullPointerException. 因爲NullPoiterException不是IOException的子類,所以它不能被catch塊捕獲,而是直接往調用層拋出去. 一種改進的寫法就是在關閉流的時候先進行非空判斷,但這樣代碼會顯得很囉嗦。我的認爲比較優雅的寫法是直接調用commons-io包提供的 IOUtils.closeQuitely()方法關閉流(或者本身封裝一個closeQuitely()方法)。ui

使用這種寫法還有一種好處,就是當遇到關閉多個IO資源時不容易出錯,好比下面這段代碼: spa

?.net

1
2
3
4
5
6
7
8
9
10
11
12
13
14
InputStream is;
OutputStream os ;
try {
     is=openInputStream();
     // do something
} catch (IOException e){
     e.printStaceTrace(e);
} finally {
     try {
         if  (is !=  null  )  is.close();
         if  (os !=  null  )  os.close();
     } catch (IOException e ){
     }
}
   

當is.close()發生錯誤的時候,os.close()就沒法被執行,從而致使os所引用的資源沒有被釋放。 code

也許Oracle也以爲這種try .. catch ... finally的樣板代碼太不必,所以在JDK 7中對try 子句進行了一些改造,免去編寫一些手動關閉資源的代碼,讓代碼看起來更緊湊更簡潔。好比上面的代碼在JDK 7下能夠改爲:orm


1
2
3
4
5
6
7
8
try (
  InputStream is = openInputStream();
  OutputStream os = openOutStream();
){
   // do something 
} catch (IOException e){
     e.printStaceTrace(e);
}
   

Oracle 把這裏的try(..)語句叫作try-with-resource語句。須要注意的是,try(.. )中變量所引用的對象都必須是實現了java.io.AutoClosable接口的實例,當退出try ..catch塊時,JDK會自動調用close()方法。 也就是說,try-with-resource語句中的resource(資源)不只限於IO資源。 對象

 

這裏有必要對try-with-resource語句的一些細節進行補充說明: blog

  • JDK會確保全部資源的close()方法被調用,無論close()方法是否拋出異常, 而調用的順序和資源聲明的順序相反接口

  • try-with-resource語句中全部拋出的異常都會被捕獲。若是多個異常被拋出,後面所拋出的異常會被suppress(抑 制)在前一個異常中,catch塊最終只拿到最早拋出的那個異常。能夠依次經過調用Throwable類定義的getSuppressed()得到被 suppressed(抑制)的異常

仍是上面那個例子,

  • 當退出try .. catch.塊的時候,JDK會先調用os.close(),而後是is.close(), 若是兩次close()都拋出IOException, 那麼is.close()所拋出的異常會被suppress(抑制)在os.close()所拋出的異常中,最終catch塊只捕獲到 os.close()所拋出的異常。能夠經過getSuppressed()方法拿到is.close()所拋出的異常。

  • 若是調用openInputStream()的時候就發生IOException,那麼openOutputStream()就不會被調 用,os.close()和is.close()也不會被調用, catch塊捕捉到 調用openInputStream()時所拋出的異常。 

  • 若是調用openOutputStream()發生IOException(用記號 e1表示), 那麼is.close()仍是會被調用,  若是此時is.close()又拋出IOException(用記號 e2表示),那麼e2會被suppress到e1中,而catch塊捕捉到的異常是 e1. 

 

除了對try塊作了改造,JDK 7還對catch部分進行了簡化,容許把多個catch子句合併。 好比: 

?

1
2
3
4
5
6
7
8
try (
  InputStream is = openInputStream();
  OutputStream os = openOutStream();
){
   // do something   
} catch (IOException  | XMLParseException | XPathException e){
     e.printStaceTrace(e);
}
   

 

此外,當你從新拋出多個異常時,再也不須要詳細定義異常類型了,編譯器已經知道你具體拋出的是哪一個異常了你只需在方法定義的時候聲明須要拋出的異常便可。好比

?

1
2
3
4
5
6
7
8
// 雖然這裏用Exception匹配拋出的IOException,到編譯器知道實際上拋給上層的異常是IOException
         public  void  doIO()  throws  IOException {
             try {
                 throw  new  IOException();
             } catch (Exception e){
                 throw  e;
             }
         }
   

PS : 這個特性我想不到會帶來什麼好處

 

JDK 7還有其餘有趣的語法新特性,好比二進制字面量,用下劃線分割長數字,泛型參數的類型推斷,switch支持字符串匹配等等。 如今JDK 8又引入了一些有用的特性。在不須要考慮向後兼容的前提下, 適當並靈活運用一些語法特性,可讓咱們的代碼在必定程度上顯得更清晰,更簡潔。

相關文章
相關標籤/搜索