Java異常機制

Hanyta發表於2024-04-30

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;
    }


}
複製程式碼


相關文章