java 判断exception类型_Checked Exception | Java语言设计者的失误?
背景
最近公司一直在執行sonar掃描代碼bug、漏洞及異味,但發現了很對異常處理的問題,大多數是對Java異常處理不正確導致的,那本文就談談Java的異常是什么?設計者的初衷又是什么?
Exception 介紹
Exception和Error都是繼承了Throwable類,在Java中只有Throwable類型的實例才可以被拋出(throw)或者捕獲(catch),它是異常處理機制的基本組成類型。
Exception和Error體現了Java平臺設計者對不同異常情況的分類。Exception是程序正常運行中,可以預料的意外情況,可能并且應該被捕獲,進行相應處理。
Error是指在正常情況下,不大可能出現的情況,絕大部分的 Error 都會導致程序(比如虛擬機自身)處于非正常的、不可恢復狀態。既然是不正常情況,所以不便于也不需要捕獲,常見的比如OutOfMemoryError之類,都是Error的子類。
Exception異常本身又分為可檢查(checkd)異常和不可檢查(uncheckd)異常。
可檢查異常在源代碼里必須顯式地進行捕獲處理,這是編譯期檢查的一部分。不可檢查的Error是Throwable,而不是Exception,通常我在編碼過程中編譯器會提示如何處理異常,類似于我們常見的try catch或者繼續throw。
不檢查異常就是所謂的運行時異常,類似NullPointerException、ArrayIndexOutOfBoundsException之類,通常是可以編碼過程中避免的代碼邏輯錯誤,具體根據需要來判斷是否需要捕獲,并不會在編譯期強制要求捕獲此類異常。
爭議點
Checkd Exception(可檢查異常)一直都是Java語言比較有爭議的一個功能。
但Java可檢查異常的提倡者認為通過檢查異常能夠確保它們從異常中恢復;而反對者卻認為因為這些錯誤都是常見的錯誤,所有它們根本無法從異常中恢復。
同時,Java8和lambdas已經問世一段時間。在它們的世界中Checkd Exception是如何使用的呢?
設計者意圖
90年代中期,Sun公司的James Gosling提出了一種新的語言(Java)。Java這門編程語言本身是一門面向服務端長期運行的編程語言,借鑒并彌補了C、C++的不足,當然異常處理也不例外。
C語言的異常處理機制,因為C本身是單返回值,異常信息通常通過一個int值來表示成功還是失敗
C++彌補了C的不足,出現異常時可以發送錯誤信號,即引入了Exception機制,出現異常、拋出異常。但C++同時帶來了另外一個問題,調用的任何一個函數都可能出現異常,即異常信息不確定。
Java設計者吸取了C++異常設計的經驗教訓,他認為必須有更好的方法,并將異常的概念引入到Java中。并認為異常本身并不重要,而在于發生了什么異常。所以Java引入了Checkd Exception;Java方法的所有者聲明異常信息,方法調用者處理異常信息,這使得Exception在Java中變成了司空見慣的事情。所以就導致了代碼中經常出現catch(e Exception){//忽略},直接捕獲并忽略異常信息,并不能使異常信息有效傳遞。
檢查異常的目的是在本地標記并迫使開發人員處理可能的異常。已檢查的異常必須在方法簽名上聲明或處理。
這是為了鼓勵軟件的可靠性和彈性。旨在從意外情況中恢復 – 除了成功以外的可預測結果,例如嘗試付款時出現InsufficientFundsException。關于實際上需要進行什么恢復,目前尚沒有明確答案。
運行時異常也包含在Java中。由于空指針、數據錯誤、非法狀態、訪問都可能在代碼中的任何地方發生,因此將它們作為RuntimeException的子類。這種異常也就是類似于C++不可檢查異常。
運行時異常可以在任何地方拋出,而無需聲明,并且更加方便。但是直接使用它們是否正確?
優缺點
這里的關鍵點是運行時和檢查異常在功能上是等效的。但已檢查異常可以執行的處理或恢復,而運行時異常則無法做到。
反對檢查異常的最大論點是,大多數異常無法修復。一個簡單的事實是,我們的子系統都是正常的,我們看不到具體實現邏輯,我們對此不負責,也無法修復其中的異常,所以不要往上層拋出可檢查異常。
尤其問題是JDBC(SQLException)和EJB RMI(RemoteException)。這些強迫性普遍存在的系統可靠性問題(實際上不是可修復的)不是按照原始的可檢查異常概念來確定可修復的突發事件,而是要廣泛聲明。
對于任何方法,失敗的可能性都包括它調用的所有子方法。潛在的故障會累積在調用鏈中。在方法簽名上聲明這些異常,并且不再為開發人員提供一個特定的和局部的返回值,讓開發人員檢查在調用鏈中傳播的受檢查異常。
大多數EJB開發人員都經歷過這種情況–整個層或整個代碼庫的方法都需要聲明異常。調用具有不同異常的方法需要調整許多方法。
許多開發人員被告知要捕獲底層代碼的異常,然后將它們重新拋出為更高級別(應用程序級別)的已檢查異常。這需要一定的工作量(每個項目最多2000個)非功能性的拋雪球塊。
于是Java開發人員吞下異常、隱藏原因、重復記錄日志、返回null,未初始化的數據都變得很普遍。大多數項目因為異常問題可能會算出上百個錯誤編碼或完全錯誤。
最終,開發人員對大量的catch塊產生了反感,這些塊本身已經成為錯誤的根源。
Checked Exception - 與功能代碼不兼容
然后我們來看看Java8,它具有新的編程范式-例如lambda、Streams功能組合。
這些特性是建立在泛型之上的——參數和返回類型被泛化,這樣迭代和流操作(forEach、map、flatMap)可以被編寫來執行一個公共操作,而不考慮對象類型。
但是,與數據類型不同,聲明的異常無法泛化。
Java中沒有提供流操作(例如Stream.map)可檢查異常,該操作需要一個lambda來聲明某些已檢查的異常,并透明地將相同的已檢查的異常傳遞給周圍的代碼。
這一直是反對檢查異常的主要要點–拋出和接收catch塊之間的所有代碼邏輯都必須意識到異常。
解決方法是在RuntimeException中包裝它,它隱藏了異常的原始類型,使得原始概念中設想的特定于異常的catch塊變得毫無用處。
最后,我們可以簡單地理解Java的新理念,注意到Java8中沒有一個新的函數接口聲明checked異常。
可檢查異常使用中注意事項
- 所有的方法盡量不要定義可檢查異常,而是通過返回錯誤信息。
- 盡量不要嘗試捕獲最頂級的Exception,盡量捕獲具體的Exception,因為代碼本身是寫給人看的,機器只是順便執行,我們應該盡量通過代碼顯示直觀的信息,而不是只是Exception,因為Exception恰恰隱藏的真正的異常信息。
- try/catch范圍盡可能小,因為它本身需要創建堆棧信息,會產生額外的性能開銷。所以只需要捕獲需要的代碼片段,盡量不要使用一個大的try包住整個代碼塊。
- 不要生吞異常。這是異常處理過程中需要特別注意,因為它可能會使出現問題后難以診斷。有時我們的主要精力都放在了主要邏輯上面,往往對異常信息疏忽或者認為該異常不會出現,我們千萬不要做這種假設,我們以為的不可能出現的細節問題,往往會無限放大。
例如下面片段,這個片段導致的問題在于沒有異常輸出也沒有日志打印,更沒有拋出來:
try{//業務邏輯
}catch(Exception?e){
//沒有任何邏輯
}
下面這段代碼的問題在于直接標準輸出異常,通過這種方式難以判斷該日志如何和出現問題的邏輯結合起來,導致難以診斷問題所在,正確的姿勢應該詳細把錯誤信息輸出到日志中。
try{//業務邏輯
}catch(Exception?e){
????e.printStackTrace()
}
結論
與以前的語言相比,Java異常在可靠性和錯誤處理方面提供了主要優勢。Java支持可靠的服務器和商業軟件,這是C/C ++無法做到的。
可檢查異常以其原始形式是試圖處理突發事件而不是失敗。值得稱贊的目標是突出顯示特定的可預測點(無法連接、找不到文件等)并確保開發人員能夠處理這些點。
但Java異常最初的概念中從未包括的是,大量系統性和不可恢復的故障。這些失敗從未被聲明為受檢查異常,這也就導致Java倡導者認為Java可檢查異常出現問題,根本原因在于開發者的使用方式存在問題。
通常,代碼中可能會發生故障,而EJB、Web、Swing/AWT容器已經通過提供最外部的失敗請求異常處理程序來解決此問題。最基本的正確策略是回滾事務并返回錯誤。
運行時異常允許對捕獲的異常進行任何可能的異常處理,但要避免限制性的編碼。使用Java異常過程中要遵循早期拋出、延遲捕獲(最外層)的最佳實踐,通過這些可以簡化編碼。
一些領先的和有影響力的Java框架現在已經明確地擺脫了檢查異常。Spring、Hibernate和現代Java框架/供應商僅使用運行時異常,而這種便利性是它們流行的主要因素。
諸如Josh Bloch(Java Collections框架)、Rod Johnson、Anders Hejlsberg(C#之父)、Gavin King和Stephen Colebourn(JodaTime)等人都反對檢查異常。
現在,在Java8中,lambda是向前邁出的基本一步。這些語言特性將控制流從內部的功能操作中抽象出來。正如我們所看到的,這使得檢查異常成為過去,即立即聲明或處理的要求。
對于開發人員而言,始終必須注意可靠性并診斷可能的故障點(突發事件),例如打開文件、數據庫連接等,這一點始終很重要。如果此時提供了良好的錯誤消息,我們將創建自診斷軟件–工程成就的巔峰之作。
但是,我們應該使用未經檢查的異常來執行此操作,并且如果必須重新拋出,則應始終使用RuntimeException或特定于應用程序的子類。
正如史蒂芬·科爾本(Stephen Colebourn)所說,如果您的項目仍在使用或提倡檢查異常,則您的技能已過期5-10年。Java本身已經在前進了。
最后一點對于Java的可檢查異常也不必要矯枉過正,因為Java的可檢查異常已經遍布于大大小小的各種組件和系統中,對于一些分布式系統,比如出現網絡等問題時,確實可以通過異常信息進行恢復,通過這種方式使我們可以構建出高質量的軟件系統。
參考資料
http://literatejava.com/exceptions/checked-exceptions-javas-biggest-mistake/
https://www.oracle.com/technical-resources/articles/enterprise-architecture/effective-exceptions-part1.html
https://testing.googleblog.com/2009/09/checked-exceptions-i-love-you-but-you.html
原創不易,隨手關注或者”在看“,誠摯感謝!
總結
以上是生活随笔為你收集整理的java 判断exception类型_Checked Exception | Java语言设计者的失误?的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: php service 函数,PHP 获
- 下一篇: matlab复杂噪声产生实验报告,mat