在没有声明的情况下,为什么在某些情况下重新抛出Throwable是合法的?

我希望以下代码在throw t;throw t;编译时错误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(); } } 

一个更复杂的示例 – 已检查的exception由外部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(); } } 

因此,当编译器可以确定捕获的exception总是合法的重新抛出时,似乎有一种特殊情况允许重新抛出exception。 它是否正确? 这在JLS中指定了哪里? 还有其他像这样模糊不清的角落吗?

这由JLS 11.2.2 (强调我的)涵盖:

抛出语句表达式是catch子句C 的最终或有效最终exception参数的throw语句可以抛出exception类E iff:

  • E是一个exception类,声明C的try语句的try块可以抛出; 和

  • E的赋值与C的任何可捕获exception类兼容 ; 和

(……)

换句话说,文档中引用的类型E可以抛出的类型,而不是捕获它的catch子句参数的类型(可捕获的exception类 )。 它只需要与catch子句参数兼容 ,但在分析中不使用参数的类型。

这就是为什么他们不遗余力说出一个最终的或有效的最终exception参数 –如果在你的例子中重新分配,分析就会消失。

因为编译器足够聪明,知道不能从try块抛出已检查的exception,因此捕获的Throwable不是必须声明的已检查exception。

请注意,自Java 7以来,这是真的,如果我没有弄错的话。

当你捕获ThrowableException并且变量实际上是final时,你可以重新抛出相同的变量,编译器将知道你可以在try {} catch块中抛出哪些已检查的exception。