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.

就像之前提到的,你能创建成群的异常并用一种通用的方式处理异常,或是你能使用特定的异常类型去区分异常并以一种准确的方式处理异常。

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