2011-05-05 179 views
14

爲什麼在Java中我們可以捕獲一個Exception即使它沒有被拋出,但我們不能捕獲它的子類(除了「未選中」RuntimeException和它的子類)。示例代碼:Java無法訪問的catch塊編譯器錯誤

class Test { 
    public static void main(String[] args) { 
     try { 
      // do nothing 
     } catch (Exception e) { 
      // OK   
     } 

     try { 
      // do nothing 
     } catch (IOException e) { 
       // COMPILER ERROR: Unreachable catch block for IOException. 
       //This exception is never thrown from the try statement body 
     }  
    } 
} 

任何想法?

+1

這意味着沒有什麼可以趕上(沒有錯誤)我看不到問題 – Neal 2011-05-05 14:58:36

+3

你應該閱讀Java語言規範,或者至少一個好的Java教程。 – 2011-05-05 14:59:11

回答

25

A RuntimeException可能會拋出任何代碼。換句話說,編譯器無法輕易預測哪種代碼可以拋出它。 RuntimeException可以被catch(Exception e)塊捕獲。

IOException但是,它是一個檢查異常 - 聲明爲拋出它的唯一方法調用可以這樣做。編譯器可以(合理地)確信它不可能發生,除非有方法調用被聲明爲拋出它。

Java編譯器根本不考慮「有一個在所有的try塊中無碼」的情況 - 它總是讓你趕上unchecked異常,在所有合理場景會有代碼可能可能會拋出未經檢查的異常。

從JLS的section 14.21

當且僅當以下兩個條件都爲真catch塊C是可達:

  • 在try塊中一些表達或throw語句是可到達的,可以扔其類型可分配給catch子句C的參數的異常。(如果包含它的最內層語句可到達,則表達式被認爲是可達的。)
  • 在try語句中沒有早先的catch塊A su ch,C的參數類型與A的參數類型相同或是其類型的子類。

按理說編譯器應該認識到,有在第一種情況下try塊內沒有表達......它看起來像這仍是一個不可到達的catch子句,給我。

編輯:正如在評論中指出,section 14.20包含此:

這是如果catch子句捕獲檢查異常類型E1編譯時錯誤,但不存在檢查的異常類型E2這樣以下所有條件成立:

  • E2 <:E1
  • try塊對應於catch子句可以拋出E2
  • 立即封閉try語句的無上述catch塊捕獲E2E2的超類型。

除非E1是異常類。

所以看起來這就是你實際上運行的犯規,但規範不明確的,因爲它可以在不可達catch塊的條款在14.21。

+0

我認爲解決方案在[§14.20](http://java.sun.com/docs/books/jls/third_edition/html/statements。html#79311),它明確指出'Exception'優於「必須拋出」檢查:「[...]除非*'E1' *是類'Exception''。 – 2011-05-05 15:22:17

+0

@Joachim:找到了。將這個添加到答案中。 – 2011-05-05 15:26:52

0

簡單的Java假設任何代碼行可以拋出一個通用的ExceptionThrowable,即。 OutOfMemoryException這是一個Error而不是Exception。同樣適用於NPE。

IOException是一個特殊的異常,只能通過託管代碼引發,所以如果你的catch塊中沒有I/O調用,你的編譯器就沒有機會去捕捉它。

只是爲了與C#世界進行比較,在C#中這樣的代碼將被編譯,但會是一個概念性的錯誤,因爲如果你沒有做任何事你不會到達catch塊。像ReSharper這樣的工具可以提醒你。

+6

區別在於'IOException'(及其所有子類型)都被**檢查**異常,因此編譯器可以準確地找出**哪些語句*可能拋出它們。 'Exception'包含'RuntimeException',因此*可以在任何語句中發生(因爲它們未被選中,不需要聲明)。 – 2011-05-05 15:03:04

2

因爲對於檢查異常,拋出它們的方法必須通過'throws'關鍵字來明確地聲明這個事實,因此如果一個塊在你的情況下沒有'throws IOException',編譯器就會得到它的信息不可能 IOException被拋出,所以無論你在捕獲後做什麼,它都將無法訪問。

3

由於無法拋出無法檢測的異常,因此無法捕獲。您可以捕獲Exception,因爲未經檢查的運行時異常是Exception,可能會拋出COULD。

1

IOException是一個檢查異常,只有IO相關的代碼拋出。由於你的try塊什麼都不做,所以不會有IO相關的事情發生,IOExceptions永遠不會被拋出,所以catch塊永遠不會被執行,編譯器也不會讓你避開它。如您所說,異常可能指任何時候可能發生的未經檢查的運行時異常。這是未檢查和已檢查異常之間的主要區別,這就是爲什麼編譯器不強制執行代碼以捕獲每個可能的運行時異常。

5

IO異常只有在編譯器預測在引發IOException的代碼中可能存在某些內容時才能捕獲。所以你會得到一個警告:IO異常從不會從try語句體中拋出(因爲try體內沒有任何東西)。