为什么不允许在Java实例初始化块中抛出异常?
当我试图在实例初始化(而非类初始化)块中引发异常时,我得到错误:为什么不允许在Java实例初始化块中抛出异常?,java,Java,当我试图在实例初始化(而非类初始化)块中引发异常时,我得到错误: initializer must be able to complete normally 尽管Java自己做,但为什么它是不被允许的 下面的示例创建了四个类。由于算术异常,类A在实例化过程中失败。这可以通过捕获来处理。对于B,也是如此,它会因NullPointerException而失败。但是当我试图在C中自己抛出一个NullPointerException时,程序不会编译。当我试图定义自己的RuntimeException时
initializer must be able to complete normally
尽管Java自己做,但为什么它是不被允许的
下面的示例创建了四个类。由于算术异常,类A
在实例化过程中失败。这可以通过捕获
来处理。对于B
,也是如此,它会因NullPointerException而失败。但是当我试图在C
中自己抛出一个NullPointerException时,程序不会编译。当我试图定义自己的RuntimeException时,我得到了与D
中相同的错误。因此:
我怎样才能像Java本身那样做呢
// -*- compile-command: "javac expr.java && java expr"; -*-
class expr
{
class A
{
int y;
{{ y = 0 / 0; }}
}
class B
{
Integer x = null;
int y;
{{ y = x.intValue(); }}
}
class C
{
{{ throw new NullPointerException(); }}
}
class Rex extends RuntimeException {}
class D
{
{{ throw new Rex(); }}
}
void run ()
{
try { A a = new A(); }
catch (Exception e) { System.out.println (e); }
try { B b = new B(); }
catch (Exception e) { System.out.println (e); }
try { C c = new C(); }
catch (Exception e) { System.out.println (e); }
try { D d = new D(); }
catch (Exception e) { System.out.println (e); }
}
public static void main (String argv[])
{
expr e = new expr();
e.run();
}
}
Java被设计成具有最小的特性,只有在有很好的理由这样做时,才会增加复杂性。Java不问;为什么不呢;我真的需要支持吗?(即使这样,有时也不会;) 初始化器块的代码必须插入到每个构造函数中,编译器知道该块无法正常完成,编译器发现很难为其生成代码 可以让编译器编译这段代码,但它不太可能有任何用处
在这种特殊情况下,这对您没有帮助,但了解 必须声明选中的异常,并且无法在静态或实例初始化器块中声明选中的异常 相反,您可以捕获并处理或包装选中的异常。(或使用技巧,重新播放) 初始值设定项必须能够正常完成 意味着必须有一个可能的代码路径不会引发异常。你的例子无条件地抛出,因此被拒绝。在其他示例中,静态分析还不足以确定它们在所有情况下都会抛出错误 比如说,
public class StaticThrow {
static int foo = 0;
{{ if (Math.sin(3) < 0.5) { throw new ArithmeticException("Heya"); } else { foo = 3; } }}
public static void main(String[] args) {
StaticThrow t = new StaticThrow();
System.out.println(StaticThrow.foo);
}
}
公共类静态抛出{
静态int foo=0;
{{if(Math.sin(3)<0.5){抛出新的算术异常(“Heya”);}否则{foo=3;}}}
公共静态void main(字符串[]args){
StaticThrow t=新的StaticThrow();
System.out.println(StaticThrow.foo);
}
}
编译,并在运行时抛出
线程“main”java.lang.arithmetricException中的异常:Heya
在StaticThrow。(StaticThrow.java:3)
位于StaticThrow.main(StaticThrow.java:5)
这意味着该实例将永远无法正确初始化。应该有一些条件可以正确初始化实例。e、 g
{ if(true) { throw new Rex(); } } //It doesn't complain here
如果抛出的异常是选中的异常,则必须将其添加到构造函数的
throws
子句中。e、 g
public class MyObject {
{
//...
throw new Exception();
//...
}
public MyObject() throws Exception {
}
}
实际上,您可以在初始化块中抛出异常,但如果异常是选中的,则必须用“throws”关键字标记所有构造函数 如果总是抛出异常,则会出现编译错误,但这样做是完全合法的: 福班{
{{
if(1 == 1) {
throw new Exception();
}
}}
public Foo() throws Exception {
}
}
希望这能澄清一些事情。来自
实例初始值设定项中的代码可能不会返回。除了在
对于匿名内部类,实例初始值设定项可能会抛出
仅当已检查的异常显式
在类中每个构造函数的throws子句中声明。
另一方面,匿名内部类中的实例初始值设定项,
可以抛出任何异常
这将导致其他语句显然无法访问,Java试图禁止这些语句。这在Java语言规范(JavaSE7)的第三部分中有介绍 如果实例初始值设定项无法完成,则为编译时错误 通常() 14.21定义了无法达到的含义。特别注意 非空块中的每一条语句都不是开关 当S前面的语句可以完成时,块是可到达的 通常 及 break、continue、return或throw语句无法完成 通常 更复杂的分析是可能的(并且仍然可能产生警告),但是这些规则是可以理解的、一致可实现的,并且不会特别限制语言的未来发展 那么,我们为什么要拒绝带有(肯定)不可访问语句的程序呢?因为它们几乎肯定代表bug(在完成的代码中)。(
if
语句的行为特别支持不可靠的条件编译。)
没有任何不可访问的语句,那么为什么实例初始值设定项必须能够正常完成(这不是构造函数支持不可实例化类的要求)?因为这需要非局部分析,而Java为了保持合理的简单性并没有这样做,并且在维护过程中可能会删除语句,或者只是重新排列代码顺序
可能值得注意的是,有一种观点认为,这种相对简单的分析以及定义的分配规则使Java变得过于复杂。以及如何创建未经检查的异常?未经检查的异常扩展了
RuntimeException
。没有好的方法可以接受一个本来会被检查的异常,比如IOException
,并将其取消检查。但是,如果您创建了一个直接或间接扩展RuntimeException
的新类型,那么您可以抛出它的一个实例,而无需添加throws
子句。编译器对代码的分析不够深入,无法看到它总是抛出。在您的示例中,您将{{{throw something;}}
放在静态初始化器中,这是显而易见的。尝试一些永远正确但不明显的东西,{{if(Math.sin(3)<0.5){throw new arithmetricexception;}else{whatever;}}}}
。谢谢<代码>如果(真)抛出…就足够了
{{
if(1 == 1) {
throw new Exception();
}
}}
public Foo() throws Exception {
}