为什么不允许在Java实例初始化块中抛出exception?

当我尝试在实例初始化(而不是类初始化)块中抛出exception时,我得到错误:

initializer must be able to complete normally 

为什么不允许Java自己呢?

以下示例创建了四个类。 由于ArithmeticException,类A在实例化期间失败。 这可以通过catch来处理。 B失败与NullPointerException失败。 但是当我尝试在C自己抛出NullPointerException时,程序无法编译。 当我尝试在D定义自己的RuntimeException时,我得到了同样的错误。 所以:

我怎么能像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(); } } 

初始化程序必须能够正常完成

意味着必须有一个不会抛出exception的可能代码路径。 您的示例无条件抛出,因此被拒绝。 在其他示例中,静态分析并不足以确定它们也会抛出所有情况。

例如,

 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); } } 

编译,并在运行时抛出

 Exception in thread "main" java.lang.ArithmeticException: Heya at StaticThrow.(StaticThrow.java:3) at StaticThrow.main(StaticThrow.java:5) 

Java旨在具有最小的function,并且只有在有充分理由的情况下才会添加复杂性。 Java不问; 为什么不呢,它问道; 我真的需要支持吗? (有时甚至没有;)

必须将初始化块的代码插入到每个构造函数中,这些构造函数具有编译器知道无法正常完成的块,而编译器发现该块很难生成代码。

可以使编译器编译此代码,但它不太可能有用。


在这个特定的情况下它不会帮助你,但知道…..很有用

必须声明已检查的exception,并且无法在静态或实例初始化块中声明已检查的exception。

相反,您可以捕获并处理或包装已检查的exception。 (或使用技巧,重新抛出)

实际上,您可以在初始化块中抛出exception,但是如果您的exception是选中的,则必须使用“throws”关键字标记所有构造函数。

如果您的exception总是被抛出,您将收到编译错误,但这样的事情是完全合法的:

class Foo {

 {{ if(1 == 1) { throw new Exception(); } }} public Foo() throws Exception { } 

}

希望这能澄清一些事情。

 { throw new Rex(); } 

这意味着实例永远不会正确初始化。 应该有一些条件可以正确初始化实例。 例如

 { if(true) { throw new Rex(); } } //It doesn't complain here 

如果抛出的exception是一个checked-exception,那么你必须将它添加到构造函数的throws子句中。 例如

 public class MyObject { { //... throw new Exception(); //... } public MyObject() throws Exception { } } 

来自http://www.artima.com/designtechniques/initializationP.html

实例初始值设定项中的代码可能不会返回。 除了匿名内部类的情况之外,只有在类中每个构造函数的throws子句中显式声明了已检查的exception时,实例初始值设定项才可以抛出已检查的exception。 另一方面,匿名内部类中的实例初始值设定项可以抛出任何exception。

Java语言规范(Java SE 7)的第8.6节对此进行了介绍。

如果实例初始化程序无法正常完成,则为编译时错误(第14.21节 )。

14.21定义了无法到达的含义。 特别注意

如果S之前的语句可以正常完成,则非空交换块中的每个其他语句S都是可到达的。

break,continue,return或throw语句无法正常完成。

更复杂的分析是可能的(并且仍然可以生成警告),但这些是一组可以理解,一致可实现的规则,并不特别限制语言的未来发展。

那么为什么我们要拒绝具有(肯定)无法访问的语句的程序呢? 因为它们几乎肯定代表错误(在完成的代码中)。 ( if语句表现得特别支持狡猾的条件编译。)

没有任何无法访问的语句,那么为什么实例初始化者必须能够正常完成(不需要构造函数以支持不可实例化的类)? 因为这需要非本地分析,而Java不会这样做以保持相当简单,并且在维护期间可能会删除声明或仅重新排列代码。

可能值得注意的是,一些观点认为Java通过这种相对简单的分析以及定义分配规则而过于复杂。

它会导致其他语句显然无法访问,Java试图禁止这些语句。