![](/img/trans.png)
[英]Why does the compiler allow throws when the method will never throw the Exception
[英]Why does the Java compiler allow exceptions to be listed in the throws section that it is impossible for the method to throw
如果有一些代碼顯然不能引發異常,則Java編譯器似乎不一致,而您編寫的周圍代碼聲明該代碼可以引發該異常。
考慮這些代碼片段。
catch
從未拋出的異常。
public void g(){
try {
} catch (FileNotFoundException e) {//any checked exception
}
}
有信息的編譯錯誤
Unreachable catch block for FileNotFoundException. This exception is never thrown from the try statement body
throws
聲明,指示從未拋出的異常。
public void g() throws FileNotFoundException{
}
它編譯良好。
因此,第一個代碼段的結果表明,編譯器可以計算方法是否可以引發throws
列表中列出的throws
。 因此,似乎編譯器故意不報告第二個片段的錯誤。 但為什么? 即使編譯器知道無法拋出這些異常,為什么編譯器仍允許您在throws
部分中編寫異常?
編譯器允許這樣做,因為方法的throws
子句是方法簽名的一部分,而不是其實現的一部分。 在使簽名保持相同的同時 , 實現可能會在某個時候進行更改 。 舊的實現可能引發了檢查異常,而新的實現則沒有。 或者,簽名的設計者可能希望為實現者提供靈活性,使其在並非總是必要時拋出已檢查的異常。
只是嘗試!
對於第二種情況,將來可能會有其他一些類重寫此方法,並且在其中編寫了可能引發此異常的代碼。
Throws
不處理異常,它指示從將調用方法的位置向上的拋出異常。 換句話說,它將只是將異常傳遞給調用方。
在try...catch
塊處理異常時,這就是Java編譯器將檢查是否有任何異常要處理在catch
塊中catch
異常。
那是兩個不同的東西,一個是Throwing ,另一個是處理異常,編譯器只會把鼻子傾斜到第二個……:p
從JavaDoc :
異常處理程序可以做的不只是打印錯誤消息或暫停程序。 他們可以執行錯誤恢復,提示用戶做出決定,或者使用鏈接的異常將錯誤傳播到更高級別的處理程序。
因此,通過提供try...catch
實現,您要求編譯器做更多的事情,而不僅僅是打印異常。
另一個具體原因:
public void testException() throws FileNotFoundException {
File file = new File("test.txt");
System.out.println(file.exists());
Scanner scanner = new Scanner(file);
}
如果您通過javap -c Test.class
觀察以上示例的編譯代碼,則會發現將創建一個Exception表。
public static void testException();
Code:
0: new #2 // class java/io/File
3: dup
4: ldc #3 // String test.txt
6: invokespecial #4 // Method java/io/File."<init>":(Ljava/lang/String;)V
9: astore_0
10: getstatic #5 // Field java/lang/System.out:Ljava/io/PrintStream;
13: aload_0
14: invokevirtual #6 // Method java/io/File.exists:()Z
17: invokevirtual #7 // Method java/io/PrintStream.println:(Z)V
20: new #8 // class java/util/Scanner
23: dup
24: aload_0
25: invokespecial #9 // Method java/util/Scanner."<init>":(Ljava/io/File;)V
28: astore_1
29: goto 37
32: astore_1
33: aload_1
34: invokevirtual #11 // Method java/io/FileNotFoundException.printStackTrace:()V
37: return
Exception table:
from to target type
20 29 32 Class java/io/FileNotFoundException
因此,當編譯器在try塊中找不到任何未引發異常的代碼時,就會出現編譯時錯誤。
如果throws
則不會生成異常表。
由於您需要其他視圖-
考慮在兩個不同的子類中使用相同簽名的兩個實現。
例如,(一個虛構的示例),
public class StudentLoader {
public abstract Student readStudentData() throws SQLException, IOException;
public static void main(String args[]) {
StudentLoader loader = getStudentLoader (); //may return any subclass instance
try {
Student s = loader.readStudentData();
}
catch(IOException e) {
//do something
}
catch(SQLException e) {
//do something
}
}
}
public class StudentFileReader extends StudentLoader {
public Student readStudentData() throws IOException {
//read from a file
}
}
public class StudentDBReader extends StudentLoader {
public Student readStudentData() throws SQLException {
//read from DB
}
}
為什么編譯器允許即使無法拋出異常,也可以在throws部分寫入異常?
即使子類實現StudentDBReader
不拋IOException
,父類StudentLoader
仍然不得不說throws IOException
,因為其他實現StudentLoader
可以扔掉它。 因此,即使該方法未引發異常,您也可以使用StudentLoader
引用(指向兩個子類實例中的任何一個)向調用方指示調用方必須處理這些異常。
在顯示方法g()
代碼段1中 ,沒有繼承范圍。 代碼就在try塊中。 如果try中的任何語句引發已檢查的Exception,則必須對其進行處理。 如果使用throws子句,則必須允許繼承范圍。 編譯器無法確定在運行時將調用哪個版本的readStudentData( )
。
我希望如果不拋出throws子句中提到的Exception,則在靜態方法的情況下編譯器應該給出錯誤,因為靜態方法不參與繼承。 我不確定為什么靜態方法中的throws子句會包含在實現中從未拋出過的異常。 無論如何它都不會被覆蓋,那么為什么不在這里拋出錯誤呢? 可能有些我想念的東西。
public class UnreachableCatchBlock {
public static void main(String[] args) {
UnreachableCatchBlock ucb = new UnreachableCatchBlock();
System.out.println(ucb.getClass().getName() + " started.");
// ucb.method1();
ucb.method2();
}
private void method1() {
try {
System.out.println(getClass().getName() + ".method1() started.");
} catch (FileNotFoundException e) {//any checked exception
}
}
private void method2() {
System.out.println(getClass().getName() + ".method2() started.");
}
}
上面的代碼按照@Raedwald答案中的說明進行編譯和運行。
我不建議將其作為答案,而只是發布我嘗試包含在注釋中的代碼的地方。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.