Java™ 教程(如何拋出異常)

如何拋出異常

在捕獲異常以前,某些代碼必須拋出一個,任何代碼均可能拋出異常:你的代碼,來自其餘人編寫的包中的代碼,例如Java平臺附帶的包或Java運行時環境,不管拋出什麼異常,它老是使用throw語句拋出。html

你可能已經注意到,Java平臺提供了許多異常類,全部類都是Throwable類的後代,而且全部類都容許程序區分在程序執行期間可能發生的各類類型的異常。java

你還能夠建立本身的異常類來表示你編寫的類中可能出現的問題,實際上,若是你是程序包開發人員,則可能必須建立本身的一組異常類,以容許用戶將程序包中可能發生的錯誤與Java平臺或其餘程序包中發生的錯誤區分開來。程序員

你還能夠建立鏈式異常,有關更多信息,請參閱「鏈式異常」部分。segmentfault

throw語句

全部方法都使用throw語句拋出異常,throw語句須要一個參數:一個throwable對象,Throwable對象是Throwable類的任何子類的實例,這是一個throw語句的例子。api

throw someThrowableObject;

讓咱們看一下上下文中的throw語句,如下pop方法取自實現公共堆棧對象的類,該方法從堆棧中刪除頂部元素並返回該對象。數組

public Object pop() {
    Object obj;

    if (size == 0) {
        throw new EmptyStackException();
    }

    obj = objectAt(size - 1);
    setObjectAt(size - 1, null);
    size--;
    return obj;
}

pop方法檢查堆棧上是否有任何元素,若是堆棧爲空(其大小等於0),則pop實例化一個新的EmptyStackException對象(java.util的成員)並拋出它,本章中的建立異常類部分介紹瞭如何建立本身的異常類,如今,你須要記住的是,你只能拋出從java.lang.Throwable類繼承的對象。oracle

請注意,pop方法的聲明不包含throws子句,EmptyStackException不是已檢查的異常,所以不須要pop來聲明它可能發生。app

Throwable類及其子類

Throwable類繼承的對象包括直接後代(直接從Throwable類繼承的對象)和間接後代(從Throwable類的子級或孫級繼承的對象),下圖說明了Throwable類的類層次結構及其最重要的子類,如你所見,Throwable有兩個直接後代:ErrorException函數

exceptions-throwable.gif

Error類

當發生Java虛擬機中的動態連接故障或其餘硬故障時,虛擬機拋出Error,簡單程序一般不會捕獲或拋出Errors工具

Exception類

大多數程序拋出並捕獲從Exception類派生的對象,Exception表示發生了問題,但這不是一個嚴重的系統問題,你編寫的大多數程序將拋出並捕獲Exception而不是Error

Java平臺定義了Exception類的許多後代,這些後表明示可能發生的各類類型的異常。例如,IllegalAccessException表示沒法找到特定方法,NegativeArraySizeException表示程序試圖建立負大小的數組。

一個Exception子類RuntimeException保留用於指示錯誤使用API​​的異常,運行時異常的一個示例是NullPointerException,當方法嘗試經過空引用訪問對象的成員時發生,未經檢查的異常 — 爭議部分討論了爲何大多數應用程序不該拋出運行時異常或RuntimeException子類。

鏈式異常

應用程序一般會經過拋出另外一個異常來響應異常,實際上,第一個異常致使第二個異常,瞭解一個異常什麼時候致使另外一個異常很是有用,鏈式異常有助於程序員執行此操做。

如下是Throwable中支持鏈式異常的方法和構造函數。

Throwable getCause()
Throwable initCause(Throwable)
Throwable(String, Throwable)
Throwable(Throwable)

initCauseThrowable構造函數的Throwable參數是致使當前異常的異常,getCause返回致使當前異常的異常,initCause設置當前異常的緣由。

如下示例顯示如何使用鏈式異常。

try {

} catch (IOException e) {
    throw new SampleException("Other IOException", e);
}

在此示例中,捕獲IOException時,會建立一個新的SampleException異常,並附加原始緣由,並將異常鏈拋出到下一個更高級別的異常處理程序。

訪問堆棧跟蹤信息

如今讓咱們假設更高級別的異常處理程序想要以本身的格式轉儲堆棧跟蹤。

定義:堆棧跟蹤提供有關當前線程的執行歷史記錄的信息,並列出在發生異常時調用的類和方法的名稱,堆棧跟蹤是一種有用的調試工具,一般在拋出異常時能夠利用它。

如下代碼顯示如何在異常對象上調用getStackTrace方法。

catch (Exception cause) {
    StackTraceElement elements[] = cause.getStackTrace();
    for (int i = 0, n = elements.length; i < n; i++) {       
        System.err.println(elements[i].getFileName()
            + ":" + elements[i].getLineNumber() 
            + ">> "
            + elements[i].getMethodName() + "()");
    }
}

Logging API

下一個代碼段記錄catch塊中發生異常的位置,可是,它不是手動解析堆棧跟蹤並將輸出發送到System.err(),而是使用java.util.logging包中的日誌記錄工具將輸出發送到文件。

try {
    Handler handler = new FileHandler("OutFile.log");
    Logger.getLogger("").addHandler(handler);
    
} catch (IOException e) {
    Logger logger = Logger.getLogger("package.name"); 
    StackTraceElement elements[] = e.getStackTrace();
    for (int i = 0, n = elements.length; i < n; i++) {
        logger.log(Level.WARNING, elements[i].getMethodName());
    }
}

建立異常類

當面對選擇要拋出的異常類型時,你可使用其餘人編寫的異常 — Java平臺提供了許多可使用的異常類 — 或者你能夠編寫本身的異常類,若是你對如下任何問題的回答是確定的,你應該編寫本身的異常類;不然,你可能會使用別人的。

  • 你是否須要Java平臺中未表示的異常類型?
  • 若是他們能夠將你的異常與其餘供應商編寫的類別所引起的異常區分開來,它會幫助用戶嗎?
  • 你的代碼是否會拋出多個相關的異常?
  • 若是你使用其餘人的異常,用戶是否能夠訪問這些異常?一個相似的問題是,你的包是否應該獨立且自包含?

一個例子

假設你正在編寫鏈表類,該類支持如下方法,其中包括:

  • objectAt(int n) — 返回列表中第n個位置的對象,若是參數小於0或大於列表中當前對象的數量,則引起異常。
  • firstObject() — 返回列表中的第一個對象,若是列表不包含任何對象,則拋出異常。
  • indexOf(Object o) — 在列表中搜索指定的Object並返回其在列表中的位置,若是傳遞給方法的對象不在列表中,則拋出異常。

鏈表類能夠拋出多個異常,而且可以經過一個異常處理程序捕獲鏈表所引起的全部異常會很方便,此外,若是你計劃在包中分發鏈表,則應將全部相關代碼打包在一塊兒,所以,鏈表應該提供本身的一組異常類。

下圖說明了鏈表拋出的異常的一個可能的類層次結構。

exceptions-hierarchy.gif

選擇超類

任何Exception子類均可以用做LinkedListException的父類,可是,快速瀏覽這些子類就會發現它們不合適,由於它們太專業化或與LinkedListException徹底無關,所以,LinkedListException的父類應該是Exception

你編寫的大多數applet和應用程序都會拋出Exception對象,Error一般用於系統中嚴重的硬錯誤,例如阻止JVM運行的錯誤。

對於可讀代碼,最好將字符串 Exception附加到從 Exception類繼承(直接或間接)的全部類的名稱。

上一篇:捕獲和處理異常

相關文章
相關標籤/搜索