Java異常的選擇:Checked Exception仍是Unchecked Exception ?

曾經聽到過關於老司機和新手程序員的區別,其中最大的一個區別就在於異常的處理。新手程序員老是天真得把世界想得太美好,基本上沒想過會出現異常的狀況,而一個經驗豐富的老司機會把最壞的打算考慮進去,給出相應的解決辦法,使得發生異常時對系統的影響下降到最小。對此,我深表認同。現實的狀況老是複雜的,並且還有不少不懷好意的人時刻準備攻擊你的系統。使用你係統的用戶越多,這種潛在的風險也就越大。java

異常處理是應對這些風險的最強有力的武器。在Java的世界裏,異常有兩種:受檢異常(checked exception)和非受檢異常(unchecked exception)。想必全部的Javaer都使用過這兩種異常,可是什麼時候使用哪一個異常缺失常常困擾程序員的頭疼問題。在此,我分享一下本身的見解,若是你有不一樣的意見,請留意探討。程序員

1.若是正常狀況下會出現,那麼使用Checked Exception;反之,則使用Unchecked Exception

這條準則是我在決定使用Checked Exception仍是Unchecked Exception的第一原則。若是API的使用者在正常使用的過程當中都會出現異常,那麼這種異常就屬於Checked Exception。由於這種異常時屬於程序執行流程衆多分支之一,API的使用者必須意識到這種狀況,並作出相應的處理。編程

舉個栗子:ui

我但願向zookeeper中建立一個節點,那麼這種狀況就隱含了兩個前提條件:spa

  • 父節點已經被建立(若是有的話)
  • 本節點還未被建立

那麼,這個API的簽名大體應該是這樣:.net

void createNode(String path,byte[] data) throws FatherNodeNotExist, NodeExist;

API的使用者看到這個簽名的定義時就會獲得一個強烈的心理暗示,我須要考慮父節點不存在和本節點已存在的狀況,那麼他就不得不顯示的去處理這兩種異常。設計

有的朋友可能會爭論說,我正常的狀況下不會出現這種狀況,由於使用這個API的前提就是先建立好父節點,然後建立本節點,那我就不用拋出兩種異常了,使用者也輕鬆了許多。但事實真的如此嗎?咱們想固然的認爲了使用者是 本身人 ,他們會乖乖的按照咱們的想法去先建立父節點,再建立本節點,若是是在一個很侷限的使用場景下,每一個人都說通過嚴格培訓的,那麼你能夠去作這樣的假設,可是我仍是不推薦你這麼作,由於這樣設計使得系統是脆弱的,不穩定的。若是能經過系統能本身避免這些錯誤,爲何不呢?何況,若是你把這個API開放給第三方的使用者,那麼狀況會更糟糕,你根本不知道他們會怎樣去使用API,這很是恐怖!code

有時候狀況會變得很複雜, 正常狀況 的鑑定變得很困難,你確定會遇到這種時候,此時就須要結合你的業務場景去權衡其中的利弊。這依賴與你的經驗和對業務場景的理解,我沒法給你一個絕對的建議,那樣是不負責任的。blog

我再舉個常見的栗子:用戶修改他擁有的資源信息。在菜譜APP中給出一個接口,讓用戶修改他菜譜的信息。那麼這裏一個隱含的條件就是用戶修改他本身的菜譜信息,他是無權限修改別人的菜譜信息的。那麼這個API的簽名多是這樣的:繼承

void updateMenu(long menuId,long uid,String title,String description...);

若是用戶嘗試去修改不屬於他的菜譜呢?咱們是否須要throws UserPermissionException之類受檢異常?我認爲是不須要的。判斷是這屬於正常狀況嗎?我認爲這不算是正常狀況。正常狀況下,客戶端調用修改信息的接口,那麼menuId必定是屬於這個用戶的。若是出現這種狀況,要麼是你係統設計的就有問題,要麼就是不懷好意的人在破壞你的系統。前者須要從新設計咱們的系統,然後者咱們更不用關係,直接拋出一個RuntimeException就能夠,由於他不算正經常使用戶。

2. 調用者中能從異常中恢復的,推薦使用受檢異常;反之,則使用非受檢異常

注意這裏的一個關鍵詞是 推薦 ,決定使用哪一種異常最爲根本的仍是第一條原則。若是第一條原則難以判斷時,才考慮調用者。這條原則和 Effective Java 中的第58條很像,若是有這本書的朋友能夠再拿出來讀讀。

我和 Effective Java #58不一樣的觀點在於,這條原則只能是 推薦 ,另外,對於全部不能恢復的狀況我都建議使用非受檢異常。我對可恢復的理解是,若是API的調用者可以處理你拋出的異常,並給出積極的響應和反饋,並指導它的使用者作出調整,那麼這就是可恢復的。不可恢復就是API的調用者沒法處理你拋出的異常,或者僅僅只是打個LOG記錄一下,不能對它的使用者作出提示,那麼均可認爲是不可恢復的。

仍是最開始的栗子,若是調用 createNode 的調用者能響應 FatherNodeNotExist ,並把這種狀況反應到終端上,那麼使用受檢異常是有積極意義的。對於不可恢復的狀況,包括編程錯誤,我建議都是用非受檢異常,這樣系統能 fail fast ,把異常對系統的影響降到最低,同時你還能得到一個完整的異常堆棧信息,何樂而不爲呢?!

基本上,這兩條原則就能幫你決定究竟是使用受檢異常仍是非受檢異常了。固然,現實的狀況很複雜,須要根據你所處的具體業務場景來判斷,經驗也是不可或缺的。在設計API的時候多問下本身這是正常狀況下出現的嗎,調用者能夠處理這個異常嗎,這會頗有幫助的!

異常處理是一個很是大的話題,除了選擇 checked exception 仍是 unchecked exception 之外,還有一些通常的通用原裝,例如:

  • 只拋出與本身有關的異常
  • 封裝底層異常
  • 儘可能在拋出異常的同時多攜帶上下文信息

這些在 Effective Java 中都有詳細的介紹,朋友能夠認真讀一下這本書,寫的很是好!

對異常處理有不一樣理解的朋友能夠給我留言,一塊兒討論,共同進步!

參考文獻:

Effective Java, 2nd Edition

This article used CC-BY-SA-3.0 license, please follow it.

 

 

 

  1. Checked異常必須被顯式地捕獲或者傳遞,如Basic try-catch-finally Exception Handling一文中所說。而unchecked異常則能夠沒必要捕獲或拋出。
  2. Checked異常繼承java.lang.Exception類。Unchecked異常繼承自java.lang.RuntimeException類。

我過去支持checked異常,可是最近我改變了個人觀點。Rod Johnson(Spring Framework),Anders Hejlsberg(C#之父),Joshua Bloch(Effective Java,條目41:避免checked異常的沒必要要的使用)和其餘一些朋友使我從新考慮了checked異常的真實價值。最近咱們嘗試在一個較大的項目中使用unchecked異常,效果還不錯。錯誤處理被集中在了少數幾個類中。會有須要本地錯誤處理的地方,而不是將異常傳播給主錯誤處理代碼。可是這種地方不會不少。因爲代碼中不會處處都是try-catch塊,咱們的代碼變得可讀性更好。換句話說,使用unchecked異常比使用checked異常減小了無用的catch-rethrow try-catch塊。總之,我建議使用unchecked異常。至少在一個工程中嘗試過。我總結了如下緣由: Unchecked異常不會使代碼顯得雜亂,由於其避免了沒必要要的try-catch塊。 Unchecked異常不會由於異常聲明彙集使方法聲明顯得雜亂。 關於容易忘記處理unchecked異常的觀點在個人實踐中沒有發生。 關於沒法獲知如何處理未聲明異常的觀點在個人實踐中沒有發生。 Unchecked異常避免了版本問題。

此處連接:http://blog.csdn.net/kingzone_2008/article/details/8535287。

 

此處貌似很不錯,異常集中處理,加入返回信息。貌似更好哦。。。我也有點支持。。。

相關文章
相關標籤/搜索