我最近发现并在博客中提到了这样一个事实,即可以通过 javac 编译器偷偷检查一个已检查的异常并将其抛出到不应抛出的地方。这在 Java 6 和 7 中编译并运行,抛出一个SQLException无throwsorcatch子句:
public class Test {
    // No throws clause here
    public static void main(String[] args) {
        doThrow(new SQLException());
    }
    static void doThrow(Exception e) {
        Test.<RuntimeException> doThrow0(e);
    }
    static <E extends Exception> void doThrow0(Exception e) throws E {
        throw (E) e;
    }
}
生成的字节码表明 JVM 并不真正关心已检查/未检查的异常:
// Method descriptor #22 (Ljava/lang/Exception;)V
// Stack: 1, Locals: 1
static void doThrow(java.lang.Exception e);
  0  aload_0 [e]
  1  invokestatic Test.doThrow0(java.lang.Exception) : void [25]
  4  return
    Line numbers:
      [pc: 0, line: 11]
      [pc: 4, line: 12]
    Local variable table:
      [pc: 0, pc: 5] local: e index: 0 type: java.lang.Exception
// Method descriptor #22 (Ljava/lang/Exception;)V
// Signature: <E:Ljava/lang/Exception;>(Ljava/lang/Exception;)V^TE;
// Stack: 1, Locals: 1
static void doThrow0(java.lang.Exception e) throws java.lang.Exception;
  0  aload_0 [e]
  1  athrow
    Line numbers:
      [pc: 0, line: 16]
    Local variable table:
      [pc: 0, pc: 2] local: e index: 0 type: java.lang.Exception
JVM 接受这一点是一回事。但我对Java 语言是否应该存在一些疑问。JLS 的哪些部分证明了这种行为?它是一个错误吗?还是 Java 语言的一个隐藏得很好的“特性”?
我的感受是:
- doThrow0()'s- <E>必然- RuntimeException在- doThrow(). 因此,不需要遵循JLS §11.2的- throws条款。- doThrow()
- RuntimeException与 赋值兼容,因此编译器不会生成- Exception强制转换(这将导致 a )。- ClassCastException