陷阱 - 捕获 Throwable 异常错误或 RuntimeException

对于没有经验的 Java 程序员的一个常见的思维模式是异常情况是有问题负担,并处理这个最好的办法是一网打尽 1 ,尽快。这导致代码如下:

....
try {
    InputStream is = new FileInputStream(fileName);
    // process the input
} catch (Exception ex) {
    System.out.println("Could not open file " + fileName);
}

上面的代码有一个重大缺陷。catch 实际上会捕获比程序员期望的更多异常。假设 fileName 的值是 null,由于应用程序中的其他地方的错误。这将导致 FileInputStream 构造函数抛出 NullPointerException。处理程序将捕获此信息,并向用户报告:

    Could not open file null

这是无益和混乱的。更糟糕的是,假设它是处理输入代码引发意外异常(已检查或未选中!)。现在,用户将获得针对打开文件时未发生的问题的误导性消息,并且可能与 I / O 完全无关。

问题的根源是程序员为 Exception 编写了一个处理程序。这几乎总是一个错误:

  • 捕获 Exception 将捕获所有已检查的异常,以及大多数未经检查的异常。
  • 捕获 RuntimeException 将捕获大多数未经检查的异常。
  • 捕获 Error 将捕获未经检查的异常,这些异常表示 JVM 内部错误。这些错误通常无法恢复,因此不应被捕获。
  • 捕获 Throwable 将捕获所有可能的异常。

捕获过多一组异常的问题是处理程序通常无法正确处理所有异常。在 Exception 等的情况下,程序员很难预测可以捕获的内容; 即期待什么。

一般情况下,正确的解决办法是处理该异常抛出。例如,你可以捕获它们并在原地处理它们:

try {
    InputStream is = new FileInputStream(fileName);
    // process the input
} catch (FileNotFoundException ex) {
    System.out.println("Could not open file " + fileName);
}

或者你可以通过封闭方法将它们声明为 thrown

捕捉 Exception 是合适的情况很少。通常出现的唯一一个是这样的:

public static void main(String[] args) {
    try {
        // do stuff
    } catch (Exception ex) {
        System.err.println("Unfortunately an error has occurred. " +
                           "Please report this to X Y Z");
        // Write stacktrace to a log file.
        System.exit(1);
    }
}

在这里,我们真正想要处理所有异常,所以捕捉 Exception(甚至 Throwable)是正确的。

1 - 也称为 Pokemon 异常处理