歡迎光臨
每天分享高質量文章

【譯】11條Java異常處理的最佳實踐

本文翻譯自Top 11 Java Exception Best Practices


在之前關於Java異常的文章中,已經探討過suppressed exceptions和Java Exceptions Tutorial兩個方面的內容。要想在實際專案中正確處理Java異常,你應該熟練掌握一些Java異常處理的最佳實踐。

Java 異常處理的最佳實踐
  1. 不要 在catch陳述句塊中壓制異常

 1public class ExceptionExample {
2    public FileInputStream testMethod1(){
3        File file = new File("test.txt");
4        FileInputStream fileInputStream = null;
5        try{
6            fileInputStream = new FileInputStream(file);
7            fileInputStream.read();
8        }catch (IOException e){         
9            return null;
10        }
11        return fileInputStream;
12    }
13    public static void main(String[] args){
14        ExceptionExample instance1 = new ExceptionExample();
15        instance1.testMethod1();
16    }
17}

在異常處理時進行異常壓制是非常不好的程式設計習慣,上面的例子中,無論丟擲什麼異常都會被忽略,以至沒有留下任何問題線索。如果在這一層次不知道如何處理異常,最好將異常重新丟擲,由上層決定如何處理異常。

 1public class ExceptionExample {
2    public FileInputStream testMethod1() throws IOException{
3        File file = new File("test.txt");
4        FileInputStream fileInputStream = null;
5        try{
6            fileInputStream = new FileInputStream(file);
7            fileInputStream.read();
8        }catch (IOException e){         
9            throw e;
10        }
11        return fileInputStream;
12    }
13    public static void main(String[] args) throws IOException{
14        ExceptionExample instance1 = new ExceptionExample();
15        instance1.testMethod1();
16    }
17}
  1. 要在方法定義分句中定義具體的異常
    按照public FileInputStream testMethod1() throws Exception{這種寫法,表示該方法會丟擲所有受檢查異常,這不是一個良好的程式設計習慣。在這種情況下,我們最好丟擲足夠具體的異常,以便呼叫者進行合適的捕獲和處理,例如public FileInputStream testMethod1() throws IOException{

  2. 捕獲具體的異常
    在呼叫其他模組時,最好捕獲由該模組丟擲的具體的異常。如果某個被呼叫模組丟擲了多個異常,那麼只捕獲這些異常的父類是不好的程式設計習慣。
    例如,如果一個模組丟擲FileNotFoundExceptionIOException,那麼呼叫這個模組的程式碼最好寫兩個catch陳述句塊分別捕獲這兩個異常,而不要只寫一個捕獲Exception的catch陳述句塊。
    正確的寫法如下:

1try {
2   //some statements
3catch(FileNotFoundException e){
4//handle here
5}
6catch(IOException e){
7//handle here
8

你最好不要這麼寫:

1try {
2   //some statements
3catch(Exception e){
4//handle here
5}
  1. 記得在finally陳述句塊中釋放資源
    當你在程式碼中建立了資料庫連線、檔案運運算元或者其他需要被及時釋放的系統資源,如果你沒有及時釋放這些資源,會影響到系統的效能。
    為了避免這種情況發生,可以使用Java 7的try(open the resources) {deal with resources}陳述句,如果你還是習慣這種老式寫法,則可以按照如下方式寫:

 1finally {
2        try {
3            if (con != null) {
4                con.close();
5            }
6            if (stat != null) {
7                stat.close();
8            }
9        } catch (SQLException sqlee) {
10            sqlee.printStackTrace();
11        }
12    }
  1. 異常會影響效能

    Performance

    異常處理的效能成本非常高,每個Java程式員在開發時都應牢記這句話。建立一個異常非常慢,丟擲一個異常又會消耗1~5ms,當一個異常在應用的多個層級之間傳遞時,會拖累整個應用的效能。

  • 僅在異常情況下使用異常;

  • 在可恢復的異常情況下使用異常;

    儘管使用異常有利於Java開發,但是在應用中最好不要捕獲太多的呼叫棧,因為在很多情況下都不需要列印呼叫棧就知道哪裡出錯了。因此,異常訊息應該提供恰到好處的資訊。

6.使用標準異常
如果使用內建的異常可以解決問題,就不要定義自己的異常。Java API提供了上百種針對不同情況的異常型別,在開發中首先盡可能使用Java API提供的異常,如果標準的異常不能滿足你的要求,這時候建立自己的定製異常。盡可能得使用標準異常有利於新加入的開發者看懂專案程式碼。

7.正確得包裝異常型別
當需要在應用重新丟擲異常時,應該正確得包裝原始異常,否則會丟失原始異常,例如下麵的例子中:

 1import java.io.IOException;
2public class HelloWorld{
3     public static void main(String []args) throws Exception{
4        try{
5            throw new IOException("IOException");    
6        }catch (IOException e){
7            throw new ExampleException1("Example Exception and " + e.getMessage());
8        }
9
10     }
11}
12class ExampleException1 extends Exception{
13    public ExampleException1(String s, Throwable t){
14        super(s,t);
15    }
16    public ExampleException1(String s){
17        super(s);
18    }
19}

這個程式的輸出為:

1Exception in thread "main" ExampleException1: Example Exception and IOException                                                                                          
2        at HelloWorld.main(HelloWorld.java:8)                   

這裡發現,IOException的呼叫棧已經丟失了,因為我們在catch陳述句塊中沒有正確包裝IOException。若將catch陳述句塊修改成下麵這樣,這可以發現原始異常的呼叫棧也被打印出來了。

1catch (IOException e){
2            throw new ExampleException1("Example Exception",e);
3        }

這時候的輸出如下:

1Exception in thread "mainExampleException1Example Exception                                                                                                
2        at HelloWorld.main(HelloWorld.java:8)                                                                                                                            
3Caused byjava.io.IOExceptionIOException                                                                                                                              
4        at HelloWorld.main(HelloWorld.java:6)          

8.避免在finally陳述句塊中丟擲異常

1try {
2  method();  //here throws first exception
3finally {
4  shutdown(); //If finally blockthrew any exception the first exception will be lost forever
5}

在上面的這個程式碼片段中,finally程式碼塊也可能再次丟擲異常。如果同時丟擲兩個異常,則第一個異常的呼叫棧會丟失。在finally陳述句塊中最好只做列印錯誤資訊或者關閉資源等操作,避免在finally陳述句塊中再次丟擲異常。

9.不要使用異常控製程式的流程
不應該使用異常控制應用的執行流程,例如,本應該使用if陳述句進行條件判斷的情況下,你卻使用異常處理,這是非常不好的習慣,會嚴重影響應用的效能。

10.不要捕獲Throwable類
在應用中不應捕獲Throwable類,Error是Throwable類的子類,當應用丟擲Errors的時候,一般都是不可恢復的情況。

11.為異常記錄合適的檔案
為應用中定義的異常定義合適的檔案,如果你寫了一個自定義的異常卻沒有檔案,其他開發者會不清楚這個異常的含義,為你定義的異常配備對應的檔案是一個非常好的習慣。

贊(0)

分享創造快樂