我偶然注意到这个 throw
语句(从一些更复杂的代码中提取)编译:
void foo() {
try {
} catch (Throwable t) {
throw t;
}
}
对于一个短暂但快乐的时刻,我认为已经检查过的异常最终决定已经死了,但它仍然很高兴:
void foo() {
try {
} catch (Throwable t) {
Throwable t1 = t;
throw t1;
}
}
try
块不会抛出已检查的异常 . 这似乎是合理的,但我的问题是,语言规范中的哪些规则描述了这种行为?据我所知,§14.18 The throw Statement明确禁止它,因为 t
表达式的类型是一个已检查的异常,并且它没有被捕获或声明被抛出 . (?)
3 回答
11.2. Compile-Time Checking of Exceptions中的JLS中详细描述了此行为:
(强调我的 . )
您的第二个示例失败,因为
t1
不是“catch
子句的异常参数” .我认为你提到的§14.18 The throw Statement中的措辞是JLS中的一个错误 - 应该用Java SE 7更新的文本,而不是 .
描述预期行为的JLS文本位于§11.2.2 Exception Analysis of Statements中:
第一个要点是相关要点;因为
catch
-clause参数t
实际上是最终的(意味着它永远不会被赋值或递增或递减;请参阅§4.12.4 final Variables),throw t
只能抛出try
块可能抛出的内容 .但正如您所说,§14.18中的编译时检查不会对此作出任何限制 . §11.2.2不决定允许什么,不允许什么;相反,它应该是对可以抛出的各种限制的后果的分析 . (这种分析确实反馈到规范的更规范的部分 - §14.18本身在其第二个子弹点中使用它 - 但§14.18不能只说“如果它抛出一个它不能的例外它是一个编译时错误抛出§11.2.2“,因为那将是循环的 . )
所以我认为需要调整§14.18以适应§11.2.2的意图 .
很好找!
这是因为Java 7中引入的Project Coin中包含的更改,允许通过重新抛出原始异常来进行常规异常处理 . 这是一个适用于Java 7但不适用于Java 6的示例:
您可以阅读整篇文章,解释更改here .