请考虑以下代码:
static void main(String[] args) {
try {
} catch (Exception e) {
throw e;
}
}
This code compiles,无需将throws Exception
添加到方法签名中。 (它的行为类似Throwable
代替Exception
。
我理解为什么可以安全地运行,因为Exception
实际上不能在try
块中抛出,因此不能抛出已检查的异常;我很想知道指定这种行为的位置。
不仅仅是throw e
永远不会到达:以下代码也会编译:
static void stillCompilesWithThrownUncheckedException() {
try {
throw new NullPointerException();
} catch (Exception e) {
throw e;
}
}
但是如果你抛出一个已检查的异常,它就不会像我期望的那样编译:
static void doesNotCompileWithThrownCheckedException() {
try {
throw new Exception();
} catch (Exception e) {
throw e; // error: unreported exception Exception; must be caught or declared to be thrown
}
}
在JLS Sec 11.2.2中,它说:
一个
throw
语句(第14.18节),其抛出的表达式具有静态类型E,并且不是最终或有效的最终异常参数,可以抛出E或抛出的表达式可以抛出的任何异常类。
我对此声明的解释是throw e
可以抛出Exception
,因为e
的静态类型是Exception
。然后,在JLS Sec 11.2.3
:
如果E是已检查的异常类且E不是方法或构造函数的throws子句中声明的某个类的子类,则方法或构造函数体可以抛出一些异常类E,这是编译时错误。 / p>
但在前两种情况下,这不是编译时错误。这种行为在语言规范中描述了什么?
编辑:将其标记为欺骗,我打算提出后续问题:为什么在第一个示例中<{1}}被认为无法访问。
答案在JLS Sec 14.21中更容易找到:
如果满足以下两个条件,则可以访问catch块C:
C的参数类型是未经检查的异常类型或Exception或Exception的超类,或者try块中的某些表达式或throw语句是可到达的,并且可以抛出一个类型可分配给该类型的已检查异常C的参数。 (如果包含它的最内层语句可以访问,则表达式是可到达的。)
表达式的正常和突然完成见§15.6。
try语句中没有先前的catch块A,因此C的参数类型与A参数类型的子类相同。
这两个都是正确的(它的类型为throw e;
,并且没有先前的catch块),所以它是“可达的”。我想将一个空的Exception
块作为特殊情况调用的努力对于这种边缘有用的构造来说太过分了。
答案 0 :(得分:1)
我相信第11.2.2节的下一段回答了这个问题:
throw
语句,其抛出的表达式是catch
子句C的最终或有效最终异常参数,可以抛出异常类E iff:
- E是一个异常类,声明C可以抛出的
try
语句的try
块;和- ...
因此,throw e;
“只能抛出”相应的try-block“可以抛出的异常,”后者由try-block中的实际语句定义。
显然,空的try-block不符合任何异常类的“can throw”部分。你的第二个例子“可以抛出”NullPointerException,并且因为catch-block“只能抛出”try-block“可以抛出”的异常,所以catch-block也只能抛出未经检查的NullPointerException。
你的第三个例子的try-block“可以抛出”java.lang.Exception本身,因此catch-block“可以抛出”java.lang.Exception,因此必须捕获或声明java.lang.Exception被抛出。