Java™ 教程(異常的優勢)

異常的優勢

如今你已經知道了什麼是異常以及如何使用它們,如今是時候瞭解在程序中使用異常的優點了。java

優勢1:將錯誤處理代碼與「常規」代碼分開

異常提供了從程序的主邏輯中分離異常發生時應該作什麼的細節的方法,在傳統的編程中,錯誤檢測、報告和處理一般會致使混亂的意大利麪代碼,例如,考慮這裏的僞代碼方法將整個文件讀入內存。程序員

readFile {
    open the file;
    determine its size;
    allocate that much memory;
    read the file into memory;
    close the file;
}

乍一看,這個功能彷佛很簡單,但它忽略瞭如下全部潛在的錯誤。編程

  • 若是沒法打開文件會怎麼樣?
  • 若是沒法肯定文件的長度會發生什麼?
  • 若是沒法分配足夠的內存會怎樣?
  • 若是讀取失敗會發生什麼?
  • 若是文件沒法關閉會發生什麼?

要處理此類狀況,readFile函數必須具備更多代碼才能執行錯誤檢測、報告和處理,下面是一個函數的例子。segmentfault

errorCodeType readFile {
    initialize errorCode = 0;
    
    open the file;
    if (theFileIsOpen) {
        determine the length of the file;
        if (gotTheFileLength) {
            allocate that much memory;
            if (gotEnoughMemory) {
                read the file into memory;
                if (readFailed) {
                    errorCode = -1;
                }
            } else {
                errorCode = -2;
            }
        } else {
            errorCode = -3;
        }
        close the file;
        if (theFileDidntClose && errorCode == 0) {
            errorCode = -4;
        } else {
            errorCode = errorCode and -4;
        }
    } else {
        errorCode = -5;
    }
    return errorCode;
}

這裏有不少錯誤檢測、報告和返回,原始的七行代碼在雜亂中丟失了,更糟糕的是,代碼的邏輯流程也已丟失,所以很難判斷代碼是否正在作正確的事情:若是函數沒法分配足夠的內存,文件是否真的被關閉?在編寫方法三個月後修改方法時,確保代碼繼續作正確的事情變得更加困難,許多程序員經過忽略它來解決這個問題 — 當程序崩潰時會報告錯誤。函數

異常使你可以編寫代碼的主流程,並在其餘地方處理異常狀況,若是readFile函數使用異常而不是傳統的錯誤管理技術,那麼它看起來更像是如下內容。code

readFile {
    try {
        open the file;
        determine its size;
        allocate that much memory;
        read the file into memory;
        close the file;
    } catch (fileOpenFailed) {
       doSomething;
    } catch (sizeDeterminationFailed) {
        doSomething;
    } catch (memoryAllocationFailed) {
        doSomething;
    } catch (readFailed) {
        doSomething;
    } catch (fileCloseFailed) {
        doSomething;
    }
}

請注意,異常不會使你無需執行檢測、報告和處理錯誤的工做,但它們確實能夠幫助你更有效地組織工做。對象

優勢2:在調用堆棧中傳播錯誤

異常的第二個優勢是可以在方法的調用堆棧中傳播錯誤報告,假設readFile方法是主程序進行的一系列嵌套方法調用中的第四個方法:method1調用method2,它調用method3,最後調用readFile內存

method1 {
    call method2;
}

method2 {
    call method3;
}

method3 {
    call readFile;
}

假設method1是惟一對readFile中可能發生的錯誤感興趣的方法,傳統的錯誤通知技術強制method2method3readFile返回的錯誤代碼傳播到調用堆棧,直到錯誤代碼最終到達method1 — 惟一感興趣的方法。資源

method1 {
    errorCodeType error;
    error = call method2;
    if (error)
        doErrorProcessing;
    else
        proceed;
}

errorCodeType method2 {
    errorCodeType error;
    error = call method3;
    if (error)
        return error;
    else
        proceed;
}

errorCodeType method3 {
    errorCodeType error;
    error = call readFile;
    if (error)
        return error;
    else
        proceed;
}

回想一下,Java運行時環境在調用堆棧中向後搜索,以查找對處理特定異常感興趣的任何方法,一個方法能夠避開在其中拋出的任何異常,從而容許調用堆棧上更遠的方法捕獲它,所以,只有關心錯誤的方法才擔憂檢測錯誤。get

method1 {
    try {
        call method2;
    } catch (exception e) {
        doErrorProcessing;
    }
}

method2 throws exception {
    call method3;
}

method3 throws exception {
    call readFile;
}

可是,正如僞代碼所示,避開異常須要中間方法的一些做用,必須在其throws子句中指定能夠在方法中拋出的任何已檢查異常。

優勢3:分組和區分錯誤類型

由於在程序中拋出的全部異常都是對象,因此異常的分組或分類是類層次結構的天然結果,Java平臺中的一組相關異常類的示例是在java.io中定義的 — IOException及其後代。IOException是最多見的,表示執行I/O時可能發生的任何類型的錯誤,它的後表明示更具體的錯誤,例如,FileNotFoundException意味着文件沒在磁盤上。

方法能夠編寫能夠處理很是特定異常的特定處理程序,FileNotFoundException類沒有後代,所以如下處理程序只能處理一種類型的異常。

catch (FileNotFoundException e) {
    ...
}

方法能夠經過在catch語句中指定任何異常的超類來基於其組或常規類型捕獲異常,例如,要捕獲全部I/O異常,不管其具體類型如何,異常處理程序都會指定IOException參數。

catch (IOException e) {
    ...
}

此處理程序將可以捕獲全部I/O異常,包括FileNotFoundExceptionEOFException等,你能夠經過查詢傳遞給異常處理程序的參數來查找有關所發生狀況的詳細信息,例如,使用如下命令打印堆棧跟蹤。

catch (IOException e) {
    // Output goes to System.err.
    e.printStackTrace();
    // Send trace to stdout.
    e.printStackTrace(System.out);
}

你甚至能夠設置一個異常處理程序來處理任何Exception

// A (too) general exception handler
catch (Exception e) {
    ...
}

Exception類接近Throwable類層次結構的頂部,所以,除了處理程序要捕獲的那些異常以外,此處理程序還將捕獲許多其餘異常。若是你但願程序執行全部操做,你可能但願以這種方式處理異常,例如,爲用戶打印出錯誤消息而後退出。

可是,在大多數狀況下,你但願異常處理程序儘量具體,緣由是,處理程序必須作的第一件事是肯定發生了什麼類型的異常,而後才能決定最佳的恢復策略。實際上,經過不捕獲特定錯誤,處理程序必須適應任何可能性,過於通用的異常處理程序經過捕獲和處理程序員沒有預料到的異常,以及處理程序沒有打算處理的異常,可使代碼更容易出錯。

如上所述,你能夠建立異常組並以通常方式處理異常,或者你可使用特定的異常類型來區分異常並以精確的方式處理異常。

總結

程序可使用異常來指示發生了錯誤,要拋出異常,請使用throw語句併爲其提供異常對象 — Throwable的後代 — 以提供有關發生的特定錯誤的信息,拋出未捕獲的已檢查異常的方法必須在其聲明中包含throws子句。

程序能夠經過結合使用trycatchfinally塊來捕獲異常。

  • try塊標識可能發生異常的代碼塊。
  • catch塊標識一個代碼塊,稱爲異常處理程序,能夠處理特定類型的異常。
  • finally塊標識了一個保證執行的代碼塊,它是關閉文件、恢復資源以及在try塊中包含代碼以後進行清理的正確位置。

try語句應包含至少一個catch塊或finally塊,而且可能有多個catch塊。

異常對象的類指示拋出的異常類型,異常對象能夠包含有關錯誤的更多信息,包括錯誤消息,使用鏈式異常時,異常能夠指向致使異常的異常,異常又能夠指向致使它的異常,依此類推。


上一篇:如何拋出異常

下一篇:I/O流

相關文章
相關標籤/搜索