簡體   English   中英

Java的try catch塊中可捕獲的Exception的規則是什么?

[英]What are the rules of catchable Exceptions in try catch blocks in Java?

我正在閱讀Java語言規范, 部分引起了我的注意:

如果catch子句可以捕獲經過檢查的異常類E1,則是編譯時錯誤,並且與catch子句相對應的try塊不能拋出作為E1的子類或超類的經過檢查的異常類除非E1是Exception或Exception的超類。

我不理解的部分上面以黑體強調

給定以下層次

class A extends Exception {}
class B extends A {}
class C extends B {}

這是我的理解:

與catch子句相對應的try塊可以拋出作為E1子類的已檢查異常類

void f() {
    try {
        throw new C();
    } catch (B b) {

    }
}

編譯很好..

但是,這是什么意思

與catch子句相對應的try塊可以拋出E1的超類的已檢查異常類

這是我的理解,不會編譯(我沒想到會編譯,但是JLS使我想像的那樣)

void f() {
    try {
        throw new A();
    } catch (B b) {  // will not compile

    }
}

由於規范不太可能是錯誤的,您能否幫助我理解我所困惑的部分的含義,最好是通過一個示例進行說明?

確實,這非常棘手。 要理解這一點,您應該回到面向對象范例中的繼承概念:

  • 如果我想創建一個ArrayList,可以用兩種不同的方式來做:
    1. 第一個是忽略所有繼承概念,並對ArrayList進行硬編碼,並將其類型設置為ArrayList本身:
       ArrayList<String> list = new ArrayList<String>(); 
    2. 第二個是繼承的概念,我們嘗試在更高級別的類/接口下實例化它:
       List<String> list = new ArrayList<String>(); 

第二種方法更好,由於許多原因,我將不予提及,因為它不在答案的范圍內,但是由於ArrayList繼承自List,因此我們可以將一個新的ArrayList實例化為List對象,但是不可能做這樣的事情因為List不會擴展或繼承自ArrayList

ArrayList<String> list = new List<String>();

同樣的想法適用於Java的可捕獲異常。 按照您的示例,如果我有:

class A extends Exception {}
class B extends A {}
class C extends B {}

當我嘗試捕獲異常時,我想首先嘗試使用較低級別的類( C )(即更具體的類),因為我對它的含義有更大的把握:

try {
    // something here that throws C
} catch (C exc) {
    // Great! Its C! I know exactly what should I do!
} catch (B exc) {
    // Not that great, it might not be that hard to figure out what caused this
} catch (A exc) {
    // I still have some idea of what might have thrown this exception
} catch (Exception e) {
    // Oh boy, its not an exception that I mapped before... It might be harder than I thought... :(
}

但是我們必須遵循ArrayList的相同思路,無法在Exceptions中接收List對象:

  • 可以通過catch (C exc)catch (B exc)catch (A exc)catch (Exception e)
  • B異常可以通過catch (B exc)catch (A exc)catch (Exception e)
  • 一個異常可以被catch (A exc)catch (Exception e)
  • 等等...

由於A比B高,因此不能被catch (B exc) 這就是為什么它不能在您的示例中編譯的原因:

void f() {
    try {
        throw new A();
    } catch (B b) {  // will not compile

    }
}

您鏈接頁面的規范內,在11.3之前有一個代碼段可以例證您的要求,因為正如我已經解釋的那樣,您正確理解了:

如果catch子句可以捕獲經過檢查的異常類E1,則是編譯時錯誤,並且與catch子句相對應的try塊不能拋出作為E1的子類或超類的經過檢查的異常類,除非E1是Exception或Exception的超類。

這意味着您無法捕獲上一個catch塊的子類,並且Java編譯器會將其視為錯誤。 例如,如果您嘗試執行此操作

try {
    throw new C();
} catch (A exc) {
    // do something here, because the exception will be caught
} catch (B exc) {
    // since the exception has been caught before and B is subclass of A,
    // this causes the compilation error that JLS was talking about:
    // you cannot catch a subclass of a class previously caught.
}

我認為,了解這一點的簡單方法是舉一個具體的例子:

public void doOpen(File file) throws IOException {
    new FileInputStream(file);
}

請注意,該方法被聲明為引發IOException而不是其子類FileNotFoundException

現在讓我們嘗試捕獲一個異常,該異常我們知道文件不存在時會拋出該異常:

try {
    doOpen(someFile);
} catch (FileNotFoundException ex) {
    // print a message
} 

JLS說:

如果catch子句可以捕獲已檢查的異常類E1 ,則這是編譯時錯誤,並且與catch子句相對應的try塊不能拋出作為E1的子類或超類的已檢查的異常類的情況並非如此,除非E1Exception或類的超類Exception

在示例代碼的catch子句中,JLS中的E1FileNotFoundException 該方法聲明為throwing IOException 如果突出顯示的子句不存在(即“ E1超類”),則該捕獲將不合法。 但它顯然應該是合法的,因為doOpen方法顯然可以拋出FileNotFoundException

注意,檢查的異常是指任何不擴展Error或RuntimeException的異常類。 簡而言之,如果您在catch中聲明了除Exception或throwable以外的已檢查異常,則編譯器將抱怨try塊永遠不會拋出Exception。

我相信E1的超類可能是指E1的實例或E1的子類可以作為E1的超類存儲的情況。 在這種情況下,編譯器可能會在聲明的邏輯之外進行進一步的檢查,以確保在抱怨之前,E1超類實例實際上不是E1或E1的子類。

如果我們談論的是語言規范,則在進一步檢查的情況下,編譯器的實際輸出可能會有所不同,但前提是對於特定說明的情況,輸出是相同的。 一個編譯器實際上可以檢查E1實例是否可以作為超類拋出,而另一個編譯器可以簡單地顯示編譯錯誤,前提是每個實現均應遵循規范。

暫無
暫無

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

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