在Java SE 7中,你可以使用單個catch
語句塊處理一種或多種類型的異常,並以改進的異常類型檢查方式來重新拋出異常。
我們先來看一段代碼:
catch (IOException ex) {
logger.log(ex);
throw ex;
catch (SQLException ex) {
logger.log(ex);
throw ex;
}
在Java SE 7發佈之前,由於變量ex
存在不同的類型,因此想要創建一個公共方法來清除重複的代碼是非常困難的。不過從Java SE 7版本開始,你可以編寫如下代碼來去除重複的代碼:
//多個異常類型之間用"|"隔開
catch (IOException|SQLException ex) {
logger.log(ex);
throw ex;
}
注意:如果一個catch
語句塊處理的異常類型超過1個,那就隱式地表示被catch
的參數變量(例如上面的ex)是一個final
的變量,你不能在catch
語句塊內對其重新賦值。
使用單個catch
語句塊處理多種異常類型比使用多個catch
語句塊,每個語句塊只處理一種類型的異常所編譯生成的字節碼更小,因此也更好。一個處理多個異常類型的catch
語句塊在被Java編譯器編譯時並不會生成重複的字節碼,字節碼中也沒有重複的異常處理程序。
與以前版本相比,Java SE 7 的編譯器能夠對再次拋出的異常(rethrown exception)做出更精確的分析。這使得你可以在一個方法聲明的throws
從句中指定更具體的異常類型。我們先來看下面的一個例子:
static class FirstException extends Exception { }
static class SecondException extends Exception { }
public void rethrowException(String exceptionName) throws Exception {
try {
if (exceptionName.equals("First")) {
throw new FirstException();
} else {
throw new SecondException();
}
} catch (Exception e) {
throw e;
}
}
這個例子中的try
語句塊可能會拋出FirstException或者SecondException類型的異常。設想一下,你想在rethrowException方法聲明的throws
從句中指定這些異常類型。在Java SE 7之前的版本,你無法做到。因爲在catch
子句中的異常參數e
是java.lang.Exception類型的,catch
子句對外拋出異常參數e
,你只能在rethrowException方法聲明的throws
從句中指定拋出的異常類型爲java.lang.Exception (或其父類java.lang.Throwable)。
不過,在Java SE 7中,你可以在rethrowException方法聲明的throws
從句中指定拋出的異常類型爲FirstException和SecondException。Java SE 7的編譯器能夠判定這個被throw
語句拋出的異常參數e
肯定是來自於try
子句,而try子句只會拋出FirstException或SecondException類型的異常。儘管catch
子句的異常參數e
是java.lang.Exception類型,但是編譯器可以判斷出它是FirstException或SecondException類型的一個實例:
public void rethrowException(String exceptionName) throws FirstException, SecondException {
try {
// ...
}
catch (Exception e) {
throw e;
}
}
不過,如果catch
捕獲的異常變量在catch
子句中被重新賦值,那麼異常類型檢查的分析將不會啓用,因此在這種情況下,你不得不在方法聲明的throws
從句中指定異常類型爲java.lang.Exception。
更具體地說,從Java SE 7開始,當你在單個catch
子句中聲明一種或多種類型的異常,並且重新拋出這些被捕獲的異常時,需符合下列條件,編譯器纔會對再次拋出的異常進行類型驗證:
try
子句會拋出該異常。- 在此之前,沒有其他的
catch
子句捕獲該異常。 - 該異常類型是
catch
子句捕獲的多個異常中的一個異常類型的父類或子類。