2010-08-23 89 views
40

Java中的異常傳播是否有任何指導原則?異常傳播指南(Java)

何時向方法簽名添加異常? 例如:如果一個異常只是在缺少必要的程序資源時拋出,並且只能在頂層處理,那麼是否通過使用此異常的所有方法通過使用erring方法的所有方法傳播它?

有沒有什麼好的做法?任何不好的做法?

對不起,如果我含糊不清,但我只是尋找一些關於異常編程風格的(一般)建議。

回答

52

準則,在過去幫了我包括:

  • 拋出異常時方法不能處理異常,更重要的是,應該由調用者進行處理。一個很好的例子出現在Servlet API中 - doGet()和doPost()在請求無法正確讀取的某些情況下拋出ServletException或IOException。這兩種方法都無法處理異常,但容器是(在大多數情況下會導致50x錯誤頁面)。
  • 冒泡例外,如果方法不能處理它。這是上述的必然結果,但適用於必須捕捉異常的方法。如果該方法無法正確處理捕獲的異常,則最好將其冒泡。
  • 立即拋出異常。這可能聽起來含糊不清,但如果遇到異常情況,那麼最好拋出一個指示原始失敗點的異常,而不是嘗試通過錯誤代碼處理失敗,直到被認爲適合拋出異常的點。換句話說,試圖最小化混合異常處理與錯誤處理。
  • 要麼記錄異常或冒泡,但不要同時執行。記錄一個異常通常表明異常堆棧已經完全展開,表明沒有發生異常的進一步冒泡。因此,不建議同時執行這兩個操作,因爲它經常會導致調試過程中令人沮喪的體驗。
  • 使用java.lang.Exception(檢查的例外)的子類時,除了呼叫方處理例外。如果調用者不處理異常,則會導致編譯器引發錯誤消息。但要小心,這通常會導致開發人員「吞食」代碼中的異常。
  • 使用java.lang.RuntimeException的子類(未經檢查的例外)發出編程錯誤信號。這裏推薦的異常類包括IllegalStateException,IllegalArgumentException,UnsupportedOperationException等。再次,人們必須小心使用異常類,如NullPointerException(幾乎總是一個不好的做法拋出一個)。
  • 使用異常類層次結構來傳遞有關各層的異常信息。通過實現層次結構,可以概括調用者中的異常處理行爲。例如,您可以使用像DomainException這樣的根異常,其中有幾個子類,如InvalidCustomerException,InvalidProductException等。這裏需要注意的是,如果將每個單獨的異常情況表示爲單獨的異常,則異常層次結構可能會非常迅速地爆炸。
  • 避免捕捉您無法處理的異常。很明顯,但很多開發人員試圖捕獲java.lang.Exception或java.lang.Throwable。由於可以捕獲所有的子類異常,因此當捕獲「全局」異常類時,應用程序的運行時行爲通常會變得模糊。畢竟,人們不想捕捉到OutOfMemoryError - 應該如何處理這種異常?
  • 小心包裝異常。重新執行異常會重置異常堆棧。除非原始原因已經提供給新的異常對象,否則它將永遠丟失。爲了保留異常堆棧,必須將原始異常對象提供給新異常的構造函數。
  • 僅在需要時將檢查的異常轉換爲未經檢查的異常。當包裝一個異常時,可能會包裝一個檢查的異常並拋出一個未選中的異常。這在某些情況下很有用,特別是當意圖中止當前正在執行的線程時。但是,在其他情況下,這可能會導致一些痛苦,因爲編譯器檢查不會執行。因此,將檢查過的異常作爲未被檢查的異常調整並不意味着盲目地完成。
+4

我不同意在重新排除異常時細節丟失。 – 2010-08-23 21:18:38

+0

@Thorbjorn,是的。沒有足夠的咖啡因:-)我已經重寫了最後一節以反映實際意圖。 – 2010-08-23 21:57:17

+0

@VineetReynolds「java.lang.Exception的使用子類(checked異常),當你除了來電處理異常」 - 你的意思是「期待」? – gumkins 2017-07-06 12:54:10

2

你應該儘快處理該方法,但它必須有意義。如果異常無法通過方法拋出,但無法處理,請將其封裝到另一個異常中並拋出此新異常。

關於異常的一個不好的做法是全部抓住它們(它不是口袋妖怪,它是java!),所以請避免catch(Exception e)或更糟糕的catch(Throwable t)

+0

你是什麼意思與「包裝在另一個例外」?爲什麼不重新拋出原始異常? – wen 2010-08-23 20:09:55

+0

@Dennetik,在java中你必須捕獲檢查異常或聲明它們被拋出。它不可避免地導致包裝(或吞嚥,這是一件壞事)。否則,你的拋出列表會變得失去控制,或者碰到一個不允許你拋出檢查異常的接口。 – Yishai 2010-08-23 20:19:37

+2

因爲有時只是重新拋出原始異常沒有任何意義,例如,如果處理到數據庫的連接以獲取實體,並且拋出一個SQLException,那麼您不希望重新拋出SQLException,而是你自己的例外之一,這在你的代碼中是有意義的。 – 2010-08-23 20:22:40