我希望以下代码在throw t;
上引发编译时错误,因为main
未声明为抛出Throwable
,但它成功编译(在Java 1.7.0_45中)如果修复了编译时错误,则生成您期望的输出。
public class Test {
public static void main(String[] args) {
try {
throw new NullPointerException();
} catch(Throwable t) {
System.out.println("Caught "+t);
throw t;
}
}
}
如果Throwable
更改为Exception
,它也会编译。
这不按预期编译:
public class Test {
public static void main(String[] args) {
try {
throw new NullPointerException();
} catch(Throwable t) {
Throwable t2 = t;
System.out.println("Caught "+t2);
throw t2;
}
}
}
编译:
public class Test {
public static void main(String[] args) {
try {
throwsRuntimeException();
} catch(Throwable t) {
System.out.println("Caught "+t);
throw t;
}
}
public static void throwsRuntimeException() {
throw new NullPointerException();
}
}
这不是:
public class Test {
public static void main(String[] args) {
try {
throwsCheckedException();
} catch(Throwable t) {
System.out.println("Caught "+t);
throw t;
}
}
public static void throwsCheckedException() {
throw new java.io.IOException();
}
}
这也编译:
public class Test {
public static void main(String[] args) throws java.io.IOException {
try {
throwsIOException();
} catch(Throwable t) {
System.out.println("Caught "+t);
throw t;
}
}
public static void throwsIOException() throws java.io.IOException {
throw new java.io.IOException();
}
}
更复杂的示例 - 已检查的异常由外部catch块捕获,而不是声明为抛出。这编译:
public class Test {
public static void main(String[] args) {
try {
try {
throwsIOException();
} catch(Throwable t) {
System.out.println("Caught "+t);
throw t;
}
} catch(java.io.IOException e) {
System.out.println("Caught IOException (outer block)");
}
}
public static void throwsIOException() throws java.io.IOException {
throw new java.io.IOException();
}
}
因此,当编译器可以确定捕获的异常总是合法重新抛出时,似乎有一种特殊情况允许重新抛出异常。它是否正确?这在JLS中指定了哪里?还有其他像这样模糊不清的角落吗?
答案 0 :(得分:10)
这由JLS 11.2.2(强调我的)所涵盖:
抛出语句的抛出语句 catch子句C的最终或有效最终异常参数可以抛出异常类E iff:
E是一个异常类,声明C的try语句的try块可以抛出;以及
E与任何C的可捕获异常类兼容;以及
(...)
换句话说,E
(doc中引用的类型)是可以抛出的类型,而不是捕获它的catch子句参数的类型(捕获异常类)。它必须与catch子句参数赋值兼容,但参数的类型不用于分析。
这就是为什么他们不遗余力地说最终或有效的最终异常参数 - 如果您的示例中的t
被重新分配,分析就会消失。
答案 1 :(得分:3)
因为编译器足够聪明,知道无法从try块抛出已检查的异常,因此捕获的Throwable不是必须声明的已检查异常。
请注意,自Java 7以来这是真的,如果我没有弄错的话。
答案 2 :(得分:1)
当您抓住Throwable
或Exception
并且变量有效最终时,您可以重新抛出相同的变量,编译器将知道您可以抛出哪些已检查的异常try {} catch
阻止。