[英]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<String> list = new ArrayList<String>();
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)
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
的子類或超類的已檢查的異常類的情況並非如此,除非E1
是Exception
或類的超類Exception
。
在示例代碼的catch子句中,JLS中的E1
是FileNotFoundException
。 該方法聲明為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.