陷阱 - 抛出 Throwable 异常错误或 RuntimeException

虽然捕捉 ThrowableExceptionErrorRuntimeException 异常是坏事,但抛出它们会更糟。

基本问题是,当你的应用程序需要处理异常时,顶级异常的存在使得很难区分不同的错误条件。例如

try {
    InputStream is = new FileInputStream(someFile);  // could throw IOException
    ...
    if (somethingBad) {
        throw new Exception();  // WRONG
    }
} catch (IOException ex) {
    System.err.println("cannot open ...");
} catch (Exception ex) {
    System.err.println("something bad happened");  // WRONG
}

问题是因为我们扔了一个 Exception 实例,我们被迫捕捉它。然而,如另一个例子所述,捕捉 Exception 是不好的。在这种情况下,很难区分如果 somethingBadtrue 而被抛出的 Exception预期情况,以及我们实际捕获未经检查的异常(例如 NullPointerException)的意外情况。

如果允许传播顶级异常,我们会遇到其他问题:

  • 我们现在必须记住我们抛出顶级的所有不同原因,并区分/处理它们。
  • ExceptionThrowable 的情况下,如果我们希望传播异常,我们还需要将这些异常添加到方法的 throws 子句中。这是有问题的,如下所述。

简而言之,不要抛出这些异常。抛出一个更具体的异常,更恰当地描述已经发生的异常事件。如果需要,请定义并使用自定义异常类。

在方法的抛出中声明 Throwable 或 Exception 是有问题的

Exception 甚至`Throwable 替换方法的 throws 子句中的一长串抛出异常是很诱人的。这是一个坏主意:

  1. 它强制呼叫者处理(或传播)Exception
  2. 我们不能再依赖编译器来告诉我们需要处理的特定已检查异常。
  3. 正确处理 Exception 很困难。很难知道可能会捕获哪些实际异常,如果你不知道可以捕获什么,则很难知道哪种恢复策略是合适的。
  4. 处理 Throwable 更加困难,因为现在你还必须应对可能永远无法恢复的潜在失败。

这个建议意味着应该避免某些其他模式。例如:

try {
    doSomething();
} catch (Exception ex) {
    report(ex);
    throw ex;
}

上述尝试记录所有异常,但没有明确地处理它们。不幸的是,在 Java 7 之前,throw ex; 语句导致编译器认为任何 Exception 都可能被抛出。这可能会强制你将封闭方法声明为 throws Exception。从 Java 7 开始,编译器知道可以(重新抛出)的异常集更小。