簡體   English   中英

為什么不允許在Java實例初始化塊中拋出異常?

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

當我嘗試在實例初始化(而不是類初始化)塊中拋出異常時,我得到錯誤:

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

初始化程序必須能夠正常完成

意味着必須有一個不會引發異常的可能代碼路徑。 您的示例無條件拋出,因此被拒絕。 在其他示例中,靜態分析不足以確定它們也會拋出所有情況。

例如,

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.<init>(StaticThrow.java:3)
        at StaticThrow.main(StaticThrow.java:5)

Java旨在具有最小的功能,並且只有在有充分理由的情況下才會添加復雜性。 Java不問; 為什么不呢,它問道; 我真的需要支持嗎? (有時甚至沒有;)

必須將初始化塊的代碼插入到每個構造函數中,這些構造函數具有編譯器知道無法正常完成的塊,而編譯器發現該條件太難以生成代碼。

編譯器可以編譯這個代碼,但它不太可能有用。


在這個特定的情況下它不會幫助你,但知道.....很有用

必須聲明已檢查的異常,並且無法在靜態或實例初始化塊中聲明已檢查的異常。

相反,您可以捕獲並處理或包裝已檢查的異常。 (或使用技巧,重新拋出)

實際上,您可以在初始化塊中拋出異常,但是如果您的異常是選中的,則必須使用“throws”關鍵字標記所有構造函數。

如果您的異常總是被拋出,您將收到編譯錯誤,但這樣的事情是完全合法的:

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

如果拋出的異常是一個checked-exception,那么你必須將它添加到構造函數的throws子句中。 例如

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

    public MyObject() throws Exception {

    }
}

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

實例初始值設定項中的代碼可能不會返回。 除了匿名內部類的情況之外,只有在類中每個構造函數的throws子句中顯式聲明了已檢查的異常時,實例初始值設定項才可以拋出已檢查的異常。 另一方面,匿名內部類中的實例初始值設定項可以拋出任何異常。

Java語言規范(Java SE 7)的第8.6節對此進行了介紹。

如果實例初始化程序無法正常完成,則為編譯時錯誤(第14.21節 )。

14.21定義了無法訪問的含義。 請特別注意

如果S之前的語句可以正常完成,則非空交換塊中的每個其他語句S都是可到達的。

break,continue,return或throw語句無法正常完成。

更復雜的分析是可能的(並且仍然可以生成警告),但這些是一組可理解的,一致可實現的規則,並不特別限制語言的未來發展。

那么為什么我們要拒絕具有(絕對)無法訪問的語句的程序? 因為它們幾乎肯定代表了錯誤(在完成的代碼中)。 if語句表現得特別支持狡猾的條件編譯。)

沒有任何無法訪問的語句,那么為什么實例初始化者必須能夠正常完成(不需要構造函數來支持不可實例化的類)? 因為這需要非本地分析,而Java不會這樣做以保持相當簡單,並且在維護期間可能會刪除聲明或僅重新排列代碼。

值得注意的是,一些觀點認為Java通過這種相對簡單的分析以及定義分配規則而過於復雜。

它會導致其他語句顯然無法訪問,Java試圖禁止這些語句。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM