本文翻譯自Top 11 Java Exception Best Practices
在之前關於Java異常的文章中,已經探討過suppressed exceptions和Java Exceptions Tutorial兩個方面的內容。要想在實際專案中正確處理Java異常,你應該熟練掌握一些Java異常處理的最佳實踐。
-
不要 在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}
-
要在方法定義分句中定義具體的異常
按照public FileInputStream testMethod1() throws Exception{
這種寫法,表示該方法會丟擲所有受檢查異常,這不是一個良好的程式設計習慣。在這種情況下,我們最好丟擲足夠具體的異常,以便呼叫者進行合適的捕獲和處理,例如public FileInputStream testMethod1() throws IOException{
。 -
捕獲具體的異常
在呼叫其他模組時,最好捕獲由該模組丟擲的具體的異常。如果某個被呼叫模組丟擲了多個異常,那麼只捕獲這些異常的父類是不好的程式設計習慣。
例如,如果一個模組丟擲FileNotFoundException和IOException,那麼呼叫這個模組的程式碼最好寫兩個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}
-
記得在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 }
-
異常會影響效能
異常處理的效能成本非常高,每個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 "main" ExampleException1: Example Exception
2 at HelloWorld.main(HelloWorld.java:8)
3Caused by: java.io.IOException: IOException
4 at HelloWorld.main(HelloWorld.java:6)
8.避免在finally陳述句塊中丟擲異常
1try {
2 method(); //here throws first exception
3} finally {
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.為異常記錄合適的檔案
為應用中定義的異常定義合適的檔案,如果你寫了一個自定義的異常卻沒有檔案,其他開發者會不清楚這個異常的含義,為你定義的異常配備對應的檔案是一個非常好的習慣。