Java7的異常處理新特性-addSuppressed()方法等


開發人員對異常處理的try-catch-finally語句塊都比較熟悉。如果在try語句塊中拋出了異常,在控制權轉移到調用棧上一層代碼之前,finally語句塊中的語句也會執行。但是finally語句塊在執行的過程中,也可能會拋出異常。如果finally語句塊也拋出了異常,那么這個異常會往上傳遞,而之前try語句塊中的那個異常就丟失了。如例:

?
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
package  test;
 
public  class  DisappearedException {
     public  void  show()  throws  BaseException {
         try  {
             Integer.parseInt( "Hello" );
         catch  (NumberFormatException e1) {
             throw  new  BaseException(e1);
         finally  {
             try  {
                 int  result =  2  0 ;
             catch  (ArithmeticException e2) {
                 throw  new  BaseException(e2);
             }
         }
     }
     public  static  void  main(String[] args)  throws  Exception {
         DisappearedException d =  new  DisappearedException();
         d.show();
     }
}
 
class  BaseException  extends  Exception {
     public  BaseException(Exception ex){
         super (ex);
     }
     private  static  final  long  serialVersionUID = 3987852541476867869L;
}

對這種問題的解決辦法一般有兩種一種是拋出try語句塊中產生的原始異常,忽略在finally語句塊中產生的異常。這么做的出發點是try語句塊中的異常才是問題的根源。如例:

?
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
package  test;
 
import  java.io.FileInputStream;
import  java.io.IOException;
 
public  class  ReadFile {
     public  static  void  main(String[] args) {
         ReadFile rf =  new  ReadFile();
         try  {
             rf.read( "F:/manifest_provider_loophole.txt" );
         catch  (BaseException2 e) {
             e.printStackTrace();
         }
     }
     public  void  read(String filename)  throws  BaseException2 {
         FileInputStream input =  null ;
         IOException readException =  null ;
         try  {
             input =  new  FileInputStream(filename);
         catch  (IOException ex) {
             readException = ex;
         finally  {
             if (input !=  null ){
                 try  {
                     input.close();
                 catch  (IOException ex2) {
                     if (readException ==  null ){
                         readException = ex2;
                     }
                 }
             }
             if (readException !=  null ){
                 throw  new  BaseException2(readException); 
             }
         }
     }
}
 
class  BaseException2  extends  Exception {
     private  static  final  long  serialVersionUID = 5062456327806414216L;
     public  BaseException2(Exception ex){
         super (ex);
     }
}

另外一種是把產生的異常都記錄下來。這么做的好處是不會丟失任何異常。在java7之前,這種做法需要實現自己的異常類,而在java7中,已經對Throwable類進行了修改以支持這種情況。在java7中為Throwable類增加addSuppressed方法。當一個異常被拋出的時候,可能有其他異常因為該異常而被抑制住,從而無法正常拋出。這時可以通過addSuppressed方法把這些被抑制的方法記錄下來。被抑制的異常會出現在拋出的異常的堆棧信息中,也可以通過getSuppressed方法來獲取這些異常。這樣做的好處是不會丟失任何異常,方便開發人員進行調試。如例:

?
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
package  test;
 
import  java.io.FileInputStream;
import  java.io.IOException;
 
public  class  ReadFile2 {
     public  static  void  main(String[] args) {
         ReadFile rf =  new  ReadFile();
         try  {
             rf.read( "F:/manifest_provider_loophole.txt" );
         catch  (BaseException2 e) {
             e.printStackTrace();
         }
     }
     public  void  read(String filename)  throws  IOException {
         FileInputStream input =  null ;
         IOException readException =  null ;
         try  {
             input =  new  FileInputStream(filename);
         catch  (IOException ex) {
             readException = ex;
         finally  {
             if (input !=  null ){
                 try  {
                     input.close();
                 catch  (IOException ex2) {
                     if (readException !=  null ){
                         readException.addSuppressed(ex2);     //注意這里
                     } else {
                         readException = ex2;
                     }
                 }
             }
             if (readException !=  null ){
                 throw  readException;
             }
         }
     }
}

這種做法的關鍵在於把finally語句中產生的異常通過 addSuppressed方法加到try語句產生的異常中。

一個catch子句捕獲多個異常

在Java7之前的異常處理語法中,一個catch子句只能捕獲一類異常。在要處理的異常種類很多時這種限制會很麻煩。每一種異常都需要添加一個 catch子句,而且這些catch子句中的處理邏輯可能都是相同的,從而會造成代碼重復。雖然可以在catch子句中通過這些異常的基類來捕獲所有的異 常,比如使用Exception作為捕獲的類型,但是這要求對這些不同的異常所做的處理是相同的。另外也可能捕獲到某些不應該被捕獲的非受檢查異常。而在 某些情況下,代碼重復是不可避免的。比如某個方法可能拋出4種不同的異常,其中有2種異常使用相同的處理方式,另外2種異常的處理方式也相同,但是不同於 前面的2種異常。這勢必會在catch子句中包含重復的代碼。

對於這種情況,Java7改進了catch子句的語法,允許在其中指定多種異常,每個異常類型之間使用“|”來分隔。如例:

?
1
2
3
4
5
6
7
8
9
10
11
package  test;
 
public  class  ExceptionHandler {
     public  void  handle(){
         try  {
             //..............
         catch  (ExceptionA | ExceptionB ab) { 
         catch  (ExceptionC c) {    
         }
     }
}

這種新的處理方式使上面提出的問題得到了很好的解決。需要注意的是,在catch子句中聲明捕獲的這些異常類中,不能出現重復的類型,也不允許其中的某個異常是另外一個異常的子類,否則會出現編譯錯誤。如果在catch子句中聲明了多個異常類,那么異常參數的具體類型是所有這些異常類型的最小上界。

關於一個catch子句中的異常類型不能出現其中一個是另外一個的子類的情況,實際上涉及捕獲多個異常的內部實現方式。比如:

?
1
2
3
4
5
public  void  testSequence() {
     try  {
         Integer.parseInt( "Hello" );
     catch  (NumberFormatException | RuntimeException e){}
}

比如上面這段代碼,雖然NumberFormatException是RuntimeException的子類,但是這段代碼是可以通過編譯的。但是,如果把catch子句中兩個異常的聲明位置調換一下,就會出現在編譯錯誤。如例:

?
1
2
3
4
5
public  void  testSequenceError() {
     try  {
         Integer.parseInt( "Hello" );
     catch  (RuntimeException | NumberFormatException e) {}
}

原因在於,編譯器的做法其實是把捕獲多個異常的catch子句轉換成了多個catch子句,在每個catch子句中捕獲一個異常。上面這段代碼相當於:

?
1
2
3
4
5
6
7
public  void  testSequenceError() {
     try  {
         Integer.parseInt( "Hello" );
     catch  (RuntimeException e) {
     catch  (NumberFormatException e) {
     }
}

來自:http://my.oschina.net/fhd/blog/324484


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM