问题描述
以下代码在 Java 13 上编译和运行:
public class CheckedExceptionSSCE {
public static void main(String[] args) {
try {
methodNoThrowsDeclaration();
} catch (Exception e) {
// why is this throw allowed?
// no throws in main()
throw e;
}
}
private static void methodNoThrowsDeclaration() {
System.out.println("doesn't throw");
}
}
为什么允许使用 throw e
?
它是否在 JLS 中的某处指定?没找到,可能是我用错关键词搜索了。
编译器是否足够聪明来推断不会抛出真正的检查异常,从而允许代码编译和运行?
解决方法
这是 Java 7 中添加的一项功能。 如果您使用 catch 子句中的变量重新抛出异常,编译器可以派生出异常的类型。由于您没有要捕获的检查异常,因此它知道 e 只能是 RuntimeException 并且不需要 throws 定义。
更多信息: https://docs.oracle.com/javase/7/docs/technotes/guides/language/catch-multiple.html
,方法:
private static void methodNoThrowsDeclaration() {
System.out.println("doesn't throw");
}
不抛出任何checked exception
,从这个SO thread可以读到:
您可以抛出未经检查的异常,而无需声明它们,如果 你真的很想。未经检查的异常扩展了 RuntimeException。
因此无需修改 main
方法签名。
从 Java 语言规范(§14.18 throw 语句)可以读到:
ThrowStatement:抛出表达式;
至少满足以下三个条件之一,或者 出现编译时错误:
-
表达式的类型是未经检查的异常类(第 11.1.1 节) 或 null 类型(第 4.1 节)。
-
throw 语句包含在 try 语句的 try 块中 (第 14.20 节)并且 try 语句不会抛出 表达式类型的例外。 (在这种情况下,我们说 抛出的值被 try 语句捕获。)
-
throw 语句包含在方法或构造函数中 声明和表达式的类型可分配(第 5.2 节)到 at 的 throws 子句(第 8.4.6 节、第 8.8.5 节)中列出的至少一种类型 声明。
您显示的代码至少符合第一个条件。但是,让我们看看以下示例,其中上述三个条件都不是 true
,即如果您这样做:
private static void methodNoThrowsDeclaration() {
System.out.println("doesn't throw");
throw new Exception();
}
那会迫使你这样做:
private static void methodNoThrowsDeclaration() throws Exception {
System.out.println("doesn't throw");
throw new Exception();
}
这反过来会导致编译器错误:
Unhandled exception: java.lang.Exception
在语句 throw e;
处。