=============================================html
原文連接: java異常處理機制 轉載請註明出處!java
=============================================程序員
一款高質量系統不只僅要考慮到其功能的完備性,同時也要兼顧正確性、健壯性、可靠性、易用性、可讀性(可理解性)、可擴展性、可複用性、兼容性、可移植性……而說到這裏面的「健壯性」就不得不提到java的異常系統。數據庫
在開發過程當中見多一些不合理使用Exception的狀況,例如:
try{}包含的代碼過多;
經過 catch(Excrption e)不對異常類型進行細分;
捕獲了異常沒有進行處理;
使用Exception對代碼流程進行控制(該問題存在爭議);
設計接口的時候老是在後面添加一個 throws Exception……
這樣的代碼也許可以使程序正常運行,在程序中也不能說是錯誤,只能定義爲使用不合理。但對於一個高質量的系統來講,毫無疑問,這樣的代碼屬於劣質代碼!
在開發過程當中,當一個方法中接收到參數以後,緊接着須要進行的就是對參數進行校驗,檢驗參數的類型、取值範圍等是否符合輸入要求。那麼若是校驗失敗,該如何進行處理呢?是直接return null?仍是爲參數設置一個默認值?又或者直接throws拋出異常?
若是拋出異常,是該拋出受檢異常仍是拋出運行異常?若是拋出運行異常,那麼又改如何選擇拋出異常的類型呢?要想弄明白這些問題,就須要瞭解java的異常系統。
java中全部的可拋出結構根據類型來劃分可分爲兩種:
Error:虛擬機錯誤,此種錯誤的發生通常比較嚴重,程序中沒法捕獲也不需進行處理。發生該錯誤只有一種結果——線程退出,若是該線程是main線程,則該程序結束。若是發生該錯誤就須要去查找觸發點,優化程序。例如:VirtualMachineError,IOError。
Exception:全部的Exception都是能夠進行捕獲處理的,可是根據是否須要進行強制捕獲可分爲受檢異常(強制捕獲/拋出)和運行時異常(無強制要求)。
運行時異常主要爲RunTimeException及其子類,除此以外都爲受檢異常。
受檢異常若是不進行捕獲或拋出則沒法進行編譯。如圖:post
對於error,咱們不用太過於關注,該錯誤的產生不屬於程序中的問題,不受開發者控制。所以在這裏不進行討論。
對於Exception,咱們須要搞清楚如下幾個問題:
一、Exception從哪裏來的?
二、如何處理捕獲到的Exception?
三、如何拋出Exception?
關於第一個問題:Exception從哪裏來的?
a)底層拋上來的。
在調用方法的時候,有些方法會拋出checkedException,這種受檢異常須要進行強制處理,要麼經過try catch進行捕獲,要麼繼續上拋。
b)在本方中主動拋出的。
當該方法在執行過程的時候發現未知足正常執行的條件,這時就能夠選擇主動new一個Exception進行主動拋出。
例如,在對參數進行校驗的時候發現參數爲null,則能夠主動拋出一個NullPointException;若是參數取值範圍不符合要求,則能夠主動拋出IllegalArgumentException。
關於第二個問題:如何處理捕獲到的Exception?
對於捕獲到的Exception大部分都是底層拋出的checkedException,由於對於UnChecketException沒有必要捕獲,即便捕獲了也沒法進行處理
那麼對於捕獲到了checkedException,咱們該如何處理呢?
對該異常進行判斷,若是在本方法中能夠進行處理,那麼就在本方法中進行處理。若是在本方法中沒法處理,那麼就根據拋出策略進行拋出
不須要上拋的狀況:修改數據庫提交事務,若是發生異常則在catch中進行事務回滾。這種狀況就是能夠在本方法進行事務處理,不須要進行上拋。
而本方法沒法處理,須要拋出異常,那麼該如何拋出呢?是拋出UnChecketException仍是checketException?
關於第三個問題:如何拋出Exception? 拋出策略是什麼?
這裏就是問題的難點,如何定義這個拋出策略?
程序員有一個通病,老是喜歡找一個萬能準則,任何問題根據該準則都能找到解決方案。可不少問題是沒有明確答案的,致使在拋出策略中找不到一個萬全之策。
拋出策略主要的矛盾是:當本方法沒法處理異常須要進行拋出的時候,是拋出UnChecketException仍是checketException?
我我的認爲,先判斷上層是否有辦法處理,若是有辦法處理則拋出CheckedException,若是沒有辦法處理,解決該異常,則直接拋出UnCheckedException。可依舊存在主觀的問題:如何判斷上層是否可以解決該異常呢?
例如:優化
public FileInputStream(File file) throws FileNotFoundException { ..... if(filePath == null) { throw new NullPointerException();//文件路徑爲null則拋出UnCheckedException } else if(file.isInvalid()) { throw new FileNotFoundException("Invalid file path");//找不到文件則拋出CheckeException } .... }
在該方法中,使用上面的拋出策略不太合適,由於沒辦法證實調用處能夠解決FileNotFoundException而解決不了NullPointerException.....
在網上找答案的時候偶然間看到一個關於該問題的帖子,才發現原來早在十四年前就已經有一批人激烈的討論過這個問題,並從2003年週一至討論到2007年,持續了四年,最終被關閉討論才結束。帖子連接:爲何 Java 中要使用 Checked Exceptions
帖子中討論的異常激烈,各抒己見,能夠稱得上百家爭鳴!每種觀點都有存在的道理,可最終好像也沒有找到答案,不過在裏面有一我的的觀點挺認同的:因此沒法處理的異常都使用UnCheckedException進行拋出,不要花費精力去精心設計如何拋出。
他舉了一些觀點支持這個結論:【tianya】論據, 這裏就不在此糾結了,相信每一個認真思考該問題的都會有本身的見解的。
寫到這裏突然有個想法,是否是咱們關注的點錯誤了?UnCheckedException 和CheckedException的表現區別在因而不是受編譯機制的檢查,若是想要被編譯機制檢測,那麼就使用CheckedException,不然使用UnCheckedException。也就是說,若是須要調用者關注該異常,那麼就使用CheckedException強制調用者進行關注並處理,若是不須要調用者關注,那麼就使UnCheckedException。
若是是這樣的話,問題便很清晰了,在實現方法的時候,對於底層上拋的CheckedException,若是可以解決則進行捕獲解決,若是解決不了就直接throws繼續上拋。對於主動拋出的異常,若是須要調用者關注並處理則拋出CheckedException,若是不須要則拋出UnCheckedException。而是否須要調用者關注該異常,就有本方法的實現者來決定了,無需考慮其餘。這個拋出策略仍是能夠的!
關於其餘的,在《Effective Java》中關於異常做者提出了一個觀點:一、努力使失敗保持原子性。在執行過程當中若是發生異常,在退出以前應該使 異常發生以前修改的數據恢復到修改以前,以避免數據錯誤!!!這個問題仍是比較嚴重的,值得重點關注。url
---END---spa