拋出異常的過程十分容易,只要將其拋出就不用理睬了。當然,有些代碼必須捕獲異常,且捕獲異常需要進行周密的計劃。
捕獲異常
如果某個異常發生的時候沒有在任何地方進行捕獲,那程序就會終止執行,並在控制檯上打印出異常信息,其中包括異常的類型和堆棧的內容。
要想捕獲一個異常,必須設置try/catch語句塊。簡單的try語句塊如下:
try {
code...
....
} catch (ExceptionType e) {
handler for this type
}
如果在try語句塊中的任何代碼拋出了一個在catch字句中說明的異常類,那麼:
- 程序將跳過try語句塊的其餘代碼。
- 程序將執行catch字句中的處理器代碼
如果在try語句塊中的代碼沒有拋出任何異常,那麼程序將跳過catch字句。
如果方法中的任何代碼拋出了一個在catch字句中沒有聲明的異常類型,那麼這個方法就會立刻退出。
爲了演示捕獲異常的處理過程,我們看一個讀取數據的典型程序代碼:
public void read(String filename) {
try {
InputStream in = new FileInputStream(filename);
int b;
while ((b = in.read()) != -1)
...
} catch (IOException exception) {
exception.printStrackTrace();
}
}
需要注意的是,try語句中的大多數代碼都很容易理解:讀取並處理字節,知道遇到文件結束符爲止。正如Java API中看到的那樣,read方法有可能拋出一個IOException異常。在這種情況下,將跳出整個while循環,進入catch字句,並生成一個棧軌跡。對於一個普通的程序來說,這樣處理異常基本上合乎情理。還有其他的選擇嗎?
通常,最好的選擇是什麼也不做,而是將異常傳遞給調用者。如果read方法出現了錯誤,就讓read方法的調用者去操心。如果採用這種處理方式,就必須聲明這個方法可能會拋出一個IOException。
public void read(String filename) throws IOExcepton {
InputStream in = new FileInputStream(filename);
int b;
while ((b = in.read()) != -1)
...
}
請記住,編譯器嚴格地執行throws說明符。如果調用了一個拋出受查異常的方法,就必須對它進行處理,或者繼續傳遞。
哪種方法更好呢?通常,應該捕獲那些知道如何處理的異常,而將那些不知道怎麼處理的異常繼續進行傳遞。
如果想傳遞一個異常,就必須在方法的首部添加一個throws說明符,以便告知調用者這個方法可能會拋出異常。
這個規則也有一個例外,如果編寫一個覆蓋超類的方法,而這個方法又沒有拋出異常,那麼這個方法就必須捕獲方法代碼中出現的每一個受查異常。不允許在子類的throws說明符中出現超類方法所列出的異常類範圍。
捕獲多個異常
在一個try語句塊中可以捕獲多個異常類型,並對不同的異常做出不同的處理。可以按照下列方式爲每個異常類型使用一個單獨的catch字句:
try {
code that might throw exceptions
} catch (FileNotFoundExcetpion e) {
...
} catch (UnknownHostException e) {
...
} catch (IOException e) {
...
}
異常對象可能包含與異常本身有關的信息。要想獲得對象的更多信息,可以試着使用
e.getMessage()
得到詳細的錯誤信息,或者使用
e.getClass().getName()
得到異常對象的實際類型。
在Java SE7中,同一個catch字句可以捕獲多個異常類型。如:
try {
code that might throw exceptions
} catch (FileNotFoundException | UnknownHostException e) {
...
} catch (IOException e) {
...
}
只有當捕獲的異常類型彼此之間不存在子類關係時才需要這個特性。
再次拋出異常與異常鏈
在catch子句中可以拋出一個異常,這樣做的目的是改變異常的類型。如果開發了一個供其他程序員使用的子系統,那麼,用於表示子系統故障的異常類型可能會產生多種解釋。ServletException就是這樣一個異常類型的例子。執行servlet的代碼可能不想知道發生錯誤的細節原因,但希望明確地知道servlet是否有問題。如:
try {
access the database
} catch (SQLException e) {
throw new ServletException("database error:" + e.getMessage());
}
這裏,ServletException用帶有異常信息文本的構造器來構造。
不過,可以有一種更好的處理方法,並且將原始異常設置爲新異常的“原因”:
try {
access the database
} catch (SQLException e) {
Throwable se = new ServletException("database error");
se.initCause(e);
throw se;
}
當捕獲到異常時,就可以使用下面這條語句重新得到原始異常:
Throwable e = se.getCause();
強烈建議使用這種包裝技術。這樣可以讓用戶拋出子系統中的高級異常,而不會丟失原始異常的細節。
有時你可能只想記錄一個異常,而將它重新拋出,而不做任何改變:
try {
access the database
} catch (Exception e) {
logger.log(level, message, e);
throw e;
}
在Java SE 7之前,這種方法存在一個問題,假設這個代碼在以下方法中:
public void updateRecord() throws SQLException
Java編譯器查看catch塊中的throw語句,然後查看e的類型,會指出這個方法可以拋出任何Exception而不只是SQLException。
現在這個問題已經有所改進。編譯器會跟蹤到e來自try塊。假設這個try塊中僅有的已檢查異常是SQLException實例,另外,假設e在catch塊中未改變,將外圍方法聲明爲throws SQLException就是合法的。
捐贈
若你感覺讀到這篇文章對你有啓發,能引起你的思考。請不要吝嗇你的錢包,你的任何打賞或者捐贈都是對我莫大的鼓勵。