Advantages of Exceptions

Advantages of Exceptions

Now that you know what exceptions are and how to use them, it’s time to learn the advantages of using exceptions in your programs.

現在你知道了異常是什麼以及如何使用它們,是時候學習在你的程序使用異常的優點。

Advantage 1: Separating Error-Handling Code from “Regular” Code

Exceptions provide the means to separate the details of what to do when something out of the ordinary happens from the main logic of a program. In traditional programming, error detection, reporting, and handling often lead to confusing spaghetti code. For example, consider the pseudocode method here that reads an entire file into memory.

當一些不尋常的事情從程序的主邏輯中發生了,異常提供了方式去隔離做什麼的詳情。在傳統的編程中,錯誤檢測、報告、處理經常導致難以理解的意大利麪條式代碼。舉個例子,考慮這裏方法的僞代碼,它讀取整個文件到內存中。

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

At first glance, this function seems simple enough, but it ignores all the following potential errors.

  • What happens if the file can’t be opened?
  • What happens if the length of the file can’t be determined?
  • What happens if enough memory can’t be allocated?
  • What happens if the read fails?
  • What happens if the file can’t be closed?

初看起來,這個功能看起來足夠簡單,但是它忽略瞭如下所有的潛在錯誤。

  • 如果文件不能被打開,會發生什麼?
  • 如果文件的長度不能被確定,會發生什麼?
  • 如果不能分配足夠的內存,會發生什麼?
  • 如果讀取文件失敗,會發生什麼?
  • 如果文件不能被關閉,會發生什麼?

To handle such cases, the readFile function must have more code to do error detection, reporting, and handling. Here is an example of what the function might look like.

爲了處理這些情況,readFile函數必須有更多的代碼去做錯誤檢測、報告、處理。這裏有一個例子,函數可能看起來像這樣。

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

There’s so much error detection, reporting, and returning here that the original seven lines of code are lost in the clutter. Worse yet, the logical flow of the code has also been lost, thus making it difficult to tell whether the code is doing the right thing: Is the file really being closed if the function fails to allocate enough memory? It’s even more difficult to ensure that the code continues to do the right thing when you modify the method three months after writing it. Many programmers solve this problem by simply ignoring it — errors are reported when their programs crash.

這裏有如此多的錯誤檢測、報告、返回,原來的7行代碼已經丟失在雜亂中。更糟的是,代碼的邏輯流程已經丟失了,因此使它很困難的去分辨代碼是否正在做正確的事情:如果分配足夠內存的函數失敗了,文件真的會被關閉嗎?更加困難的是,當你在寫完它的三個月後去修改這個方法,還要確保代碼將繼續去做正確的事情。一些程序員解決這個問題靠簡單的忽略它 - 當他們的程序崩潰時錯誤將會被報告。

Exceptions enable you to write the main flow of your code and to deal with the exceptional cases elsewhere. If the readFile function used exceptions instead of traditional error-management techniques, it would look more like the following.

異常類使你書寫代碼的主要流程,並且在其它地方去處理異常情形。如果readFile函數使用異常代替傳統的錯誤管理技術,它可能看起來更像如下。

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

Note that exceptions don’t spare you the effort of doing the work of detecting, reporting, and handling errors, but they do help you organize the work more effectively.

注意異常沒有使你免受做檢測、報告、處理錯誤工作的效果,但是它們幫助你更有效的組織這些工作。

Advantage 2: Propagating Errors Up the Call Stack

A second advantage of exceptions is the ability to propagate error reporting up the call stack of methods. Suppose that the readFile method is the fourth method in a series of nested method calls made by the main program: method1 calls method2, which calls method3, which finally calls readFile.

異常的第二個優點是具有沿着方法的調用棧傳播錯誤報告的能力。假設readFile方法是在主程序中一系列嵌套的方法調用中的第四個方法:method1調用method2method2調用method3method3最終調用readFile

method1 {
    call method2;
}

method2 {
    call method3;
}

method3 {
    call readFile;
}

Suppose also that method1 is the only method interested in the errors that might occur within readFile. Traditional error-notification techniques force method2 and method3 to propagate the error codes returned by readFile up the call stack until the error codes finally reach method1—the only method that is interested in them.

同樣假設method1是唯一對可能發生在readFile中的錯誤有興趣的方法。傳統的錯誤通知技術強制method2method3沿着調用棧去傳播從readFile中返回的錯誤碼,直到錯誤碼最終到達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;
}

Recall that the Java runtime environment searches backward through the call stack to find any methods that are interested in handling a particular exception. A method can duck any exceptions thrown within it, thereby allowing a method farther up the call stack to catch it. Hence, only the methods that care about errors have to worry about detecting errors.

回想Java運行時環境向後查找調用棧去尋找對處理特定異常感覺興趣的任意方法。一個方法能迴避從中拋出的任意異常,從而允許調用棧上的更深一層方法去捕獲它。因此只有那些關心錯誤的方法應當爲檢測錯誤而擔憂。

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

method2 throws exception {
    call method3;
}

method3 throws exception {
    call readFile;
}

However, as the pseudocode shows, ducking an exception requires some effort on the part of the middleman methods. Any checked exceptions that can be thrown within a method must be specified in its throws clause.

儘管如此,就像僞代碼展示的那樣,相對於中間人方法來說,迴避異常需要一些努力。在方法中可以拋出的任意受檢查異常,必須在它的throws字句指定它們。

Advantage 3: Grouping and Differentiating Error Types

Because all exceptions thrown within a program are objects, the grouping or categorizing of exceptions is a natural outcome of the class hierarchy. An example of a group of related exception classes in the Java platform are those defined in java.io — IOException and its descendants. IOException is the most general and represents any type of error that can occur when performing I/O. Its descendants represent more specific errors. For example, FileNotFoundException means that a file could not be located on disk.

因爲在程序中被拋出的所有異常都是對象,異常的分組或分類是類層次結構的一個自然結果。一個例子是,在Java平臺中一組相關的異常類被定義在java.io中 - IOException和它的子孫。IOException是最普遍的,表示在執行I/O時可能發生的一些錯誤類型。它的子孫表示了更加具體的錯誤。舉個例子,FileNotFoundException意味着一個文件不能在磁盤上被定位到。

A method can write specific handlers that can handle a very specific exception. The FileNotFoundException class has no descendants, so the following handler can handle only one type of exception.

一個方法可以寫特殊的處理器來處理一個非常具體的異常。FileNotFoundException類沒有子孫,所以下列的處理器僅僅能處理一種異常類型。

catch (FileNotFoundException e) {
    ...
}

A method can catch an exception based on its group or general type by specifying any of the exception’s superclasses in the catch statement. For example, to catch all I/O exceptions, regardless of their specific type, an exception handler specifies an IOException argument.

一個方法能夠捕獲一個異常,基於它的分組或通用類型,通過在catch語句中指定這個異常的任意父類。舉個例子,爲了捕獲所有的I/O異常,不管這些異常的具體類型,異常處理器可以指定一個IOException參數。

catch (IOException e) {
    ...
}

This handler will be able to catch all I/O exceptions, including FileNotFoundException, EOFException, and so on. You can find details about what occurred by querying the argument passed to the exception handler. For example, use the following to print the stack trace.

這個處理器將能夠捕獲所有的I/O異常,包括FileNotFoundExceptionEOFException等等。你能找到發生了什麼的詳情,通過查詢傳遞給異常處理器的參數。舉個例子,使用如下代碼去打印堆棧軌跡。

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

You could even set up an exception handler that handles any Exception with the handler here.

你甚至可以使用這裏的處理器,去設置一個異常處理器來處理所有Exception類型的異常。

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

The Exception class is close to the top of the Throwable class hierarchy. Therefore, this handler will catch many other exceptions in addition to those that the handler is intended to catch. You may want to handle exceptions this way if all you want your program to do, for example, is print out an error message for the user and then exit.

Exception類靠近Throwable類層次結構的頂部。因此,除了打算捕獲的那些異常,這個處理器將會捕獲許多其它的異常。你可能想這樣去處理異常,如果所有你想要的就是程序這樣做,舉個例子,向用戶打印錯誤消息然後退出。

In most situations, however, you want exception handlers to be as specific as possible. The reason is that the first thing a handler must do is determine what type of exception occurred before it can decide on the best recovery strategy. In effect, by not catching specific errors, the handler must accommodate any possibility. Exception handlers that are too general can make code more error-prone by catching and handling exceptions that weren’t anticipated by the programmer and for which the handler was not intended.

然而在大多數情形下,你想要異常處理器儘可能的具體。這個原因是,處理器必須做的第一件事是在它能決定最好的恢復策略之前,去判斷髮生異常的類型。實際上,在沒有捕獲特定的錯誤時,處理器必須容納任何的可能性。太通用的處理器,通過捕獲和處理沒有被程序員預料到的和對這些沒有打算的處理器的異常,能使代碼更加易出錯。

As noted, you can create groups of exceptions and handle exceptions in a general fashion, or you can use the specific exception type to differentiate exceptions and handle exceptions in an exact fashion.

就像之前提到的,你能創建成羣的異常並用一種通用的方式處理異常,或是你能使用特定的異常類型去區分異常並以一種準確的方式處理異常。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章