陷阱 - 抛出 Throwable 异常错误或 RuntimeException
虽然捕捉 Throwable
,Exception
,Error
和 RuntimeException
异常是坏事,但抛出它们会更糟。
基本问题是,当你的应用程序需要处理异常时,顶级异常的存在使得很难区分不同的错误条件。例如
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
是不好的。在这种情况下,很难区分如果 somethingBad
是 true
而被抛出的 Exception
的预期情况,以及我们实际捕获未经检查的异常(例如 NullPointerException
)的意外情况。
如果允许传播顶级异常,我们会遇到其他问题:
- 我们现在必须记住我们抛出顶级的所有不同原因,并区分/处理它们。
- 在
Exception
和Throwable
的情况下,如果我们希望传播异常,我们还需要将这些异常添加到方法的throws
子句中。这是有问题的,如下所述。
简而言之,不要抛出这些异常。抛出一个更具体的异常,更恰当地描述已经发生的异常事件。如果需要,请定义并使用自定义异常类。
在方法的抛出中声明 Throwable 或 Exception 是有问题的
用 Exception
甚至`Throwable 替换方法的 throws
子句中的一长串抛出异常是很诱人的。这是一个坏主意:
- 它强制呼叫者处理(或传播)
Exception
。 - 我们不能再依赖编译器来告诉我们需要处理的特定已检查异常。
- 正确处理
Exception
很困难。很难知道可能会捕获哪些实际异常,如果你不知道可以捕获什么,则很难知道哪种恢复策略是合适的。 - 处理
Throwable
更加困难,因为现在你还必须应对可能永远无法恢复的潜在失败。
这个建议意味着应该避免某些其他模式。例如:
try {
doSomething();
} catch (Exception ex) {
report(ex);
throw ex;
}
上述尝试记录所有异常,但没有明确地处理它们。不幸的是,在 Java 7 之前,throw ex;
语句导致编译器认为任何 Exception
都可能被抛出。这可能会强制你将封闭方法声明为 throws Exception
。从 Java 7 开始,编译器知道可以(重新抛出)的异常集更小。