Java異常的分類和類結構圖
Java標準庫內建了一些通用的異常,這些類以Throwable為頂層父類。Throwable又派生出Error類和Exception類。錯誤:Error類以及他的子類的例項,代表了JVM本身的錯誤。錯誤不能被程式設計師通過程式碼處理,Error很少出現。因此,程式設計師應該關注Exception為父類的分支下的各種異常類。異常:Exception以及他的子類,代表程式執行時傳送的各種不期望發生的事件。可以被Java異常處理機制使用,是異常處理的核心。
總體上我們根據Javac對異常的處理要求,將異常類分為2類。
非檢查異常(unckecked exception):Error 和 RuntimeException 以及他們的子類。javac在編譯時,不會提示和發現這樣的異常,不要求在程式處理這些異常。所以如果願意,我們可以編寫程式碼處理(使用try…catch…finally)這樣的異常,也可以不處理。對於這些異常,我們應該修正程式碼,而不是去通過異常處理器處理 。這樣的異常發生的原因多半是程式碼寫的有問題。如除0錯誤ArithmeticException,錯誤的強制型別轉換錯誤ClassCastException,陣列索引越界ArrayIndexOutOfBoundsException,使用了空物件NullPointerException等等。
檢查異常(checked exception):除了Error 和 RuntimeException的其它異常。javac強制要求程式設計師為這樣的異常做預備處理工作(使用try…catch…finally或者throws)。在方法中要麼用try-catch語句捕獲它並處理,要麼用throws子句宣告丟擲它,否則編譯不會通過。這樣的異常一般是由程式的執行環境導致的。因為程式可能被執行在各種未知的環境下,而程式設計師無法干預使用者如何使用他編寫的程式,於是程式設計師就應該為這樣的異常時刻準備著。如SQLException , IOException,ClassNotFoundException 等。
注意:檢查異常是在編譯時必須進行處理的異常,否則編譯不會通過,要求強制進行處理。非檢查異常(Runtime Exception)是執行時才會出現的異常,不需要在程式中做強制處理。非檢查異常可以不做處理,即不要求在程式中用try catch捕獲或者用throws自己丟擲。
異常處理的基本語法
在編寫程式碼處理異常時,對於檢查異常,有2種不同的處理方式:使用try…catch…finally語句塊處理它。或者,在函式簽名中使用throws 宣告交給函式呼叫者caller去解決。
try…catch…finally語句塊
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | try { //try塊中放可能發生異常的程式碼。 //如果執行完try且不發生異常,則接著去執行finally塊和finally後面的程式碼(如果有的話)。 //如果發生異常,則嘗試去匹配catch塊。 } catch (SQLException SQLexception){ //每一個catch塊用於捕獲並處理一個特定的異常,或者這異常型別的子類。Java7中可以將多個異常宣告在一個catch中。 //catch後面的括號定義了異常型別和異常引數。如果異常與之匹配且是最先匹配到的,則虛擬機器將使用這個catch塊來處理異常。 //在catch塊中可以使用這個塊的異常引數來獲取異常的相關資訊。異常引數是這個catch塊中的區域性變數,其它塊不能訪問。 //如果當前try塊中發生的異常在後續的所有catch中都沒捕獲到,則先去執行finally,然後到這個函式的外部caller中去匹配異常處理器。 //如果try中沒有發生異常,則所有的catch塊將被忽略。 } catch (Exception exception){ //... } finally { //finally塊通常是可選的。 //無論異常是否發生,異常是否匹配被處理,finally都會執行。 //一個try至少要有一個catch塊,否則, 至少要有1個finally塊。但是finally不是用來處理異常的,finally不會捕獲異常。 //finally主要做一些清理工作,如流的關閉,資料庫連線的關閉等。 } |
finally塊
finally塊不管異常是否發生,只要對應的try執行了,則它一定也執行。只有一種方法讓finally塊不執行:System.exit()。因此finally塊通常用來做資源釋放操作:關閉檔案,關閉資料庫連線等等。
良好的程式設計習慣是:在try塊中開啟資源,在finally塊中清理釋放這些資源。
需要注意的地方:
1、finally塊沒有處理異常的能力。處理異常的只能是catch塊。
2、在同一try…catch…finally塊中 ,如果try中丟擲異常,且有匹配的catch塊,則先執行catch塊,再執行finally塊。如果沒有catch塊匹配,則先執行finally,然後去外面的呼叫者中尋找合適的catch塊。
3、在同一try…catch…finally塊中 ,try發生異常,且匹配的catch塊中處理異常時也丟擲異常,那麼後面的finally也會執行:首先執行finally塊,然後去外圍呼叫者中尋找合適的catch塊。
finally塊和return
首先一個不容易理解的事實:在 try塊中即便有return,break,continue等改變執行流的語句,finally也會執行。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | public static void main(String[] args) { int re = bar(); System.out.println(re); } private static int bar() { try { return 5 ; } finally { System.out.println( "finally" ); } } /*輸出: finally */ |
注意:try .. catch ..finally語句塊中至少有一個try,至少有一個catch或者finally。如果catch沒有捕獲到異常,會直接在finally中處理。
<1>finally中的return會覆蓋try或catch塊中的return返回值。
<2>如果在try中使用了return就不用執行catch中的語句,但是永遠會執行finally中的語句,如果try中無retrun語句,在catch中丟擲異常,而finally中使用了retrun,則catch中的語句不會被執行。
<3>如果同時在catch和finally中丟擲異常,finally中丟擲的異常
- 不要在fianlly中使用return。
- 不要在finally中丟擲異常。
- 減輕finally的任務,不要在finally中做一些其它的事情,finally塊僅僅用來釋放資源是最合適的。
- 將盡量將所有的return寫在函式的最後面,而不是try … catch … finally中。
自定義異常
自定義異常可以按照如上圖方式,定義一個父類業務異常繼承自RuntimeException,子類異常繼承自父類,父類異常中定義code,message,date屬性。
RuntimeException原始碼:
public class RuntimeException extends Exception {
static final long serialVersionUID = -7034897190745766939L;
/** Constructs a new runtime exception with {@code null} as its
* detail message. The cause is not initialized, and may subsequently be
* initialized by a call to {@link #initCause}.
*/
public RuntimeException() {
super();
}
/** Constructs a new runtime exception with the specified detail message.
* The cause is not initialized, and may subsequently be initialized by a
* call to {@link #initCause}.
*
* @param message the detail message. The detail message is saved for
* later retrieval by the {@link #getMessage()} method.
*/
public RuntimeException(String message) {
super(message);
}
/**
* Constructs a new runtime exception with the specified detail message and
* cause. <p>Note that the detail message associated with
* {@code cause} is <i>not</i> automatically incorporated in
* this runtime exception's detail message.
*
* @param message the detail message (which is saved for later retrieval
* by the {@link #getMessage()} method).
* @param cause the cause (which is saved for later retrieval by the
* {@link #getCause()} method). (A <tt>null</tt> value is
* permitted, and indicates that the cause is nonexistent or
* unknown.)
* @since 1.4
*/
public RuntimeException(String message, Throwable cause) {
super(message, cause);
}
/** Constructs a new runtime exception with the specified cause and a
* detail message of <tt>(cause==null ? null : cause.toString())</tt>
* (which typically contains the class and detail message of
* <tt>cause</tt>). This constructor is useful for runtime exceptions
* that are little more than wrappers for other throwables.
*
* @param cause the cause (which is saved for later retrieval by the
* {@link #getCause()} method). (A <tt>null</tt> value is
* permitted, and indicates that the cause is nonexistent or
* unknown.)
* @since 1.4
*/
public RuntimeException(Throwable cause) {
super(cause);
}
/**
* Constructs a new runtime exception with the specified detail
* message, cause, suppression enabled or disabled, and writable
* stack trace enabled or disabled.
*
* @param message the detail message.
* @param cause the cause. (A {@code null} value is permitted,
* and indicates that the cause is nonexistent or unknown.)
* @param enableSuppression whether or not suppression is enabled
* or disabled
* @param writableStackTrace whether or not the stack trace should
* be writable
*
* @since 1.7
*/
protected RuntimeException(String message, Throwable cause,
boolean enableSuppression,
boolean writableStackTrace) {
super(message, cause, enableSuppression, writableStackTrace);
}
}複製程式碼
public class BusinessException extends RuntimeException {
/**
* 業務資訊編碼
*/
private String code;
/**
* 業務資訊編碼
*/
private String errorMessage;
public BusinessException(String errorMessage) {
super(errorMessage);//呼叫父類帶參建構函式,必須呼叫給Throwable超類中的message賦值,否則通過e.getMessage將取不到值
this.errorMessage = errorMessage;
}
}
複製程式碼