Java異常處理:如何寫出“正確”但被編譯器認為有語法錯誤的程式
文章的標題看似自相矛盾,然而我在“正確”二字上打了引號。我們來看一個例子,關於Java異常處理(Exception Handling)的一些知識點。
看下面這段程式。方法pleaseThrow接受一個Exception的例項,然後簡單地將該例項丟擲。然後呼叫這個方法時,我傳入了一個SQLException的例項。因為pleaseThrow的呼叫包裹在一個try catch塊裡,
問題:plesseThrow方法丟擲的SQLException可以成功被catch住麼?
public class ExceptionForQuiz<T extends Exception> { private void pleaseThrow(final Exception t) throws T { throw (T) t; } public static void main(final String[] args) { try { new ExceptionForQuiz<RuntimeException>().pleaseThrow(new SQLException()); } catch( final SQLException ex){ System.out.println("Jerry print"); ex.printStackTrace(); } } }
答案:上面這段程式碼有語法錯誤,不能透過編譯!
我們來一步步分析。
Java類ExceptionForQuiz<T extends Exception>使用了一個泛型語法,T extends Exception意思是這個泛型類例項化的時候,傳入的型別引數T必須是Exception以及它的子類。
我在例項化類ExceptionForQuiz時,傳入的型別引數是RuntimeException。
RuntimeException在Java裡是一種Unchecked異常,即使一個方法執行時可能會丟擲RuntimeException,也不需要開發人員在方法前用程式碼顯式宣告。
看JDK RuntimeException的註釋說的很清楚:Unchecked exceptions do NOT need to be declared in a method or constructor's clause if they can be thrown by the execution of the method or constructor.
這個作者Frank Yellin一定是個大牛。
因為泛型是 Java 1.5 版本才引進的概念,關於泛型有一個型別擦除的概念,即 泛型資訊只存在於程式碼編譯階段,編譯之後的程式碼裡,與泛型相關的資訊會被擦除掉。 比如之前泛型類中的型別引數部分如果沒有指定上限,像這種寫法<T>, 則會被轉譯成普通的Object型別。如果指定了上限如<T extends String>則型別引數就被替換成型別上限。
為了簡化起見,我們先把程式碼裡的try catch塊去掉。
下面是從ExceptionForQuiz.class反編譯之後的程式碼:
我們從上圖能觀察到,方法pleaseThrow和雷ExceptionForQuiz的泛型引數RuntimeException已經被擦除掉了。pleaseThrow這個方法能丟擲的異常型別已經被擦除成為Exception了。
使用javap觀察編譯生成的位元組碼,同樣能發現型別引數RuntimeException被擦除的事實:
看第二個紅色高亮區域:Exceptions: throw java.lang.Exception
現在我們來看編譯器會報什麼錯誤訊息:Unreachable catch block for SQLException. This exception is never thrown from the try statement body.
根據異常型別擦除的事實,這個錯誤訊息是合理的,因為pleaseThrow方法的宣告現在只能丟擲型別為Exception的異常,所以第14行的catch永遠也沒有辦法接收到型別為SQLException的異常,所以編譯器丟擲錯誤。
如何消除掉這個編譯器錯誤呢?把第14行的SQLException改成RuntimeException即可。
但是這樣的話,雖然消除了語法錯誤,但是方法pleaseThrow丟擲的SQLException沒有辦法被catch住,會報執行時錯誤:
如何把pleaseThrow丟擲的SQLException也用catch語句接住呢?將第14行的RuntimeException改成所有異常的超類:Exception。
再次執行,這次既沒有語法錯誤,也沒有執行時錯誤了:SQLException已經成功地被第14行的catch語句捕捉住了。
來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/24475491/viewspace-2213410/,如需轉載,請註明出處,否則將追究法律責任。
相關文章
- 正確理解PHP程式編譯時的錯誤資訊PHP編譯
- 處理JavaScript異常的正確姿勢JavaScript
- 異常錯誤資訊處理
- Oracle異常錯誤處理Oracle
- ORACLE 異常錯誤處理Oracle
- 前端工作流編譯正確操作流程和錯誤處理記錄前端編譯
- PHP錯誤處理和異常處理PHP
- 翻譯 | Java流中如何處理異常Java
- 深入探討Java中的異常與錯誤處理Java
- pl/sql中錯誤的異常處理SQL
- Python錯誤處理和異常處理(二)Python
- rust學習十、異常處理(錯誤處理)Rust
- C++編譯錯誤的正確查詢方式C++編譯
- php錯誤與異常處理方法PHP
- goang 錯誤&異常處理機制Go
- C++錯誤和異常處理C++
- Laravel Exceptions——異常與錯誤處理LaravelException
- Java基礎知識:通過異常處理錯誤Java
- [譯]Go如何優雅的處理異常Go
- 總結:整理 oracle異常錯誤處理 .Oracle
- PHP系列(七)PHP錯誤異常處理PHP
- PHP編譯安裝時常見錯誤解決辦法,php編譯常見錯誤PHP編譯
- 用Java寫編譯器(1)- 詞法和語法分析Java編譯語法分析
- 編譯器有關的Makefile語法編譯
- JAVA 異常處理Java
- JAVA異常處理Java
- Java 異常處理Java
- 在vue使用異常處理做錯誤提示Vue
- .NET----錯誤和異常處理機制
- 前端錯誤收集以及統一異常處理前端
- PHP異常、錯誤處理機制筆記PHP筆記
- java安全編碼指南之:異常處理Java
- 被誤刪的檔案正確處理方法,快速找回誤刪的檔案
- C++編譯器怎麼實現異常處理1 (轉)C++編譯
- C++編譯器怎麼實現異常處理3 (轉)C++編譯
- C++編譯器怎麼實現異常處理2 (轉)C++編譯
- 如何正確處理nonce
- Java程式異常處理的特殊情況Java