我最近发现并在博客中提到了这样一个事实,即可以通过 javac 编译器偷偷检查一个已检查的异常并将其抛出到不应抛出的地方。这在 Java 6 和 7 中编译并运行,抛出一个SQLException
无throws
orcatch
子句:
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