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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/13532794/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-31 13:06:48  来源:igfitidea点击:

Why is it not allowed to throw an exception in a Java instance initialization block?

java

提问by ceving

When I try to throw an exception in an instance initialization (not class initialization) block I get the error:

当我尝试在实例初始化(不是类初始化)块中抛出异常时,出现错误:

initializer must be able to complete normally

Why is it not allowed although Java does it itself?

为什么 Java 自己做却不允许?

The following example creates four classes. The class Afails during instantiation because of an ArithmeticException. This can be and handled with a catch. The same for Bwhich fails with a NullPointerException. But when I try to throw a NullPointerException on my own as in Cthe program does not compile. And I get the same error when I try to define my own RuntimeException as in D. So:

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

How can I do the same as Java does itself?

我该如何做与 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();
    }
}

回答by Daniel Fischer

initializer must be able to complete normally

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

means that there must be a possible code path that doesn't throw an exception. Your examples unconditionally throw, and are therefore rejected. In the other examples, the static analysis doesn't go far enough to determine that they also throw in all cases.

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

For example,

例如,

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

compiles, and when run throws

编译,当运行抛出

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

回答by Peter Lawrey

Java is designed to have minimal features and complexity is only added when there is a very good reason to do so. Java doesn't ask; why not, it asks; Do I really need to support this? (and even then doesn't sometimes ;)

Java 被设计为具有最少的功能,并且只有在有充分理由的情况下才会增加复杂性。Java 不问;为什么不呢,它问;我真的需要支持这个吗?(即使那样有时也不会;)

The code for an initialiser block must be inserted into each constructor, having a block which the compiler knows doesn't complete normally to a condition the compiler finds too difficult to generate code for.

初始化块的代码必须插入到每个构造函数中,编译器知道有一个块不能正常完成到编译器发现难以为其生成代码的情况。

The compiler could be made to compile this code but its unlikely to be of any use.

编译器可以编译这段代码,但它不太可能有任何用处。



It won't help you in this specific case but it useful to know that.....

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

Checked exceptions must be declared and there is no way to declare checked exception in a static or an instance initialiser block.

必须声明受检异常,并且无法在静态或实例初始化程序块中声明受检异常。

Instead you can catch and handle, or wrap the checked exception. (Or using tricks, rethrow it)

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

回答by comanitza

You are actually allowed to throw an exception in an initialization block, but you must mark all constructors with the "throws" keyword, if your exception is a checked one.

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

You will get an compiling error if your exception will always be thrown, but something like this is perfectly legal:

如果你的异常总是被抛出,你会得到一个编译错误,但这样的事情是完全合法的:

class Foo {

类 Foo {

{{
    if(1 == 1) {
        throw new Exception();
    }
}}

public Foo() throws Exception {

}

}

}

Hope this clarifies a few things.

希望这可以澄清一些事情。

回答by AlexWien

From http://www.artima.com/designtechniques/initializationP.html

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

The code inside an instance initializer may not return. Except in the case of anonymous inner classes, an instance initializer may throw checked exceptions only if the checked exceptions are explicitly declared in the throws clause of every constructor in the class. Instance initializers in anonymous inner classes, on the other hand, can throw any exception.

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

回答by Tom Hawtin - tackline

This is covered by section 8.6of the Java Language Specification (Java SE 7).

Java 语言规范 (Java SE 7)的第 8.6 节涵盖了这一点。

It is a compile-time error if an instance initializer cannot complete normally (§14.21).

如果实例初始值设定项无法正常完成(第14.21 节),则会出现编译时错误。

14.21 defines what it means to be unreachable. Note in particular

14.21 定义了无法访问的含义。特别注意

Every other statement S in a non-empty block that is not a switch block is reachable iff the statement preceding S can complete normally.

非空块(不是 switch 块)中的每个其他语句 S 都是可达的,如果 S 之前的语句可以正常完成。

and

A break, continue, return, or throw statement cannot complete normally.

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

More complicated analysis would be possible (and could still generate warnings), but these are a set of rules which are understandable, consistently implementable and don't restrict future development of the language particularly.

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

So why would we want to reject programs with (definitely) unreachable statements? Because they almost certainly represent bugs (in finished code). (ifstatements behave peculiarly to support dodgy conditional compilation.)

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

There aren't any unreachable statements, so why must instance initialers be able to complete normally (not a requirement for constructors in order to support non-instantiable classes)? Because that requires non-local analysis which Java does not do in order to remain reasonably simple and a statement may be dropped in or just order of code rearranged during maintenance.

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

It is probably worth noting that a body of opinion believes that Java is overcomplicated by this relatively simple analysis, together with definitie assignment rules.

可能值得注意的是,一些观点认为 Java 被这种相对简单的分析以及明确的赋值规则过于复杂了。

回答by Bhesh Gurung

{ throw new Rex(); }

That means that the instance will be never initialized properly. There should be some condition where the instance can be initialized properly. e.g.

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

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


If the exception being thrown is a checked-exception then you must add it to the constructor's throwsclause. e.g.

如果抛出的异常是受检异常,则必须将其添加到构造函数的throws子句中。例如

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

    public MyObject() throws Exception {

    }
}

回答by irreputable

It'll cause the rest of statements apparently unreachable, which Java tries to forbid.

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