[Java異常處理的10個最佳實踐](http://www.importnew.com/20139.html)
[TOC=1,3]
異常處理在編寫健壯的 Java 應用中扮演著非常重要的角色。異常處理并不是功能性需求,它需要優雅地處理任何錯誤情況,比如資源不可用、非法的輸入、null 輸入等等。Java 提供很多異常處理特性,通過內置的 try、catch、finally關鍵字實現。Java 同樣允許創建新的異常和使用 throw 和 throws 拋出該異常。在實踐中,異常處理不單單是知道語法這么簡單。編寫健壯的代碼更像是一種藝術,而不是一門科學,在接下來的文章中,我們將討論 Java 異常處理的最佳實踐。這些最佳實踐遵循標準的 JDK 庫,以及幾個開源代碼庫,來更好地應對錯誤和異常處理。這還是為 Java 程序員提供的編寫健壯代碼的袖珍參考手冊。
**編程中異常處理的最佳實踐**
這里是我收集的 Java 編程中異常處理的 10 個最佳實踐。大家對 Java 中的受檢異常(checked Exception)褒貶不一,這種語言特性要求該異常必須被處理。在本文中,我們盡可能少使用受檢異常,同時也要學會在 Java 編程中,區別使用受檢和非受檢異常。
1)為可恢復的錯誤使用受檢異常,為編程錯誤使用非受檢異常。
對 Java 開發者來說,選擇受檢還是非受檢異常總是讓人感到困惑。受檢異常保證你會針對錯誤情況提供異常處理代碼,這是一種從語言層面上強制你編寫健壯代碼的一種方式,但同時也引入大量雜亂的代碼并導致其可讀性變差。當然,如果你有可替代方式或恢復策略的話,捕獲異常并做處理看起來似乎也合情合理。在 Java 編程中選擇受檢異常還是運行時異常的更多信息,請參考 checked vs unchecked exceptions。
2)在 finally 程序塊中關閉或者釋放資源
這是 Java 編程中一個廣為人知的最佳實踐和一個事實上的標準,尤其是在處理網絡和 IO 操作的時候。在 finally 塊中關閉資源能保證無論是處于正常還是異常執行的情況下,資源文件都能被合理釋放,這由 finally 語句塊保證。從 Java7 開始,新增加了一項更有趣的功能:自動資源管理,或者稱之為ARM塊。盡管如此,我們仍然要記住在 finally 塊中關閉資源,這對于釋放像 FileDescriptors 這類資源至關重要,因為它在 socket 和文件操作中都會被用到。
3)在堆棧信息中包含引起異常的原因
Java 庫和開源代碼在很多情況下會將一種異常包裝成另一種異常。這樣記錄和打印根異常就變得非常重要。Java 異常類提供了 getCause() 方法來獲取導致異常的原因,這可以提供更多有關異常發生的根本原因的信息。這條實踐對調試或排除故障大有幫助。在把一個異常包裝成另一種異常時,記住需要把源異常傳遞給新異常的構造器。
4)始終提供異常的有意義的完整信息
異常信息是最重要的,在其中,你能找到問題產生的原因,因為這是出問題后程序員最先看到的地方。記得始終提供精確的真實的信息。例如,對比下面兩條 IllegalArgumentException 的異常信息:
message 1: “Incorrect argument for method” message 2: “Illegal value for ${argument}: ${value}
第一條消息僅說明了參數是非法的或不正確的,但第二條消息包括了參數名和非法值,這對找到錯誤原因很重要。在編寫異常處理代碼的時候,應當始終遵循該 Java 最佳實踐。
5)避免過度使用受檢異常
受檢異常的強制性在某種程度上具有一定的優勢,但同時它也使得代碼可讀性變差,混淆了正常的業務邏輯代碼。你可以通過適度使用受檢異常來最大限度地減少這類情況的發生,這樣可以得到更簡潔的代碼。你同樣可以使用 Java7 的新功能,比如在一個catch語句中捕獲多個異常,以及自動管理資源,以此來移除一些冗余的代碼。
6)將受檢異常轉為運行時異常
這是在諸如 Spring 之類的框架中用來減少使用受檢異常的方式之一,大部分 JDBC 的受檢異常都被包裝進 DataAccessException 中,DataAccessException異常是一種非受檢異常。這個最佳實踐帶來的好處是可以將特定的異常限制到特定的模塊中,比如把 SQLException 拋到 DAO 層,把有意義的運行時異常拋到客戶端層。
7)記住異常的性能代價高昂
需要記住的一件事是異常代價高昂,同時讓代碼運行緩慢。假如你有一個方法從 ResultSet 中進行讀取,它經常會拋出 SQLException 而不是將 cursor 移到下一元素,這將會比不拋出異常的正常代碼執行的慢的多。因此最大限度的減少不必要的異常捕捉,去修復真正的根本問題。不要僅僅是拋出和捕捉異常,如果你能使用 boolean 變量去表示執行結果,可能會得到更整潔、更高性能的解決方案。修正錯誤的根源,避免不必要的異常捕捉。
8)避免空的 catch 塊
沒有什么比空的 catch 塊更糟糕的了,因為它不僅隱藏了錯誤和異常,同時可能導致你的對象處于不可用狀態或者臟狀態。空的 catch 塊沒有意義,除非你非常肯定異常不會以任何方式影響對象的狀態,但在程序執行期間,用日志記錄錯誤依然是最好的方法。這在 Java 異常處理中不僅僅是一個最佳實踐,而且是一個最通用的實踐。
9)使用標準異常
第九條最佳實踐是建議使用標準和內置的 Java 異常。使用標準異常而不是每次創建我們自己的異常,這對于目前和以后代碼的可維護性和一致性,都是最好的選擇。重用標準異常使代碼可讀性更好,因為大部分 Java 開發人員對標準的異常更加熟悉,比如 JDK 中的RuntimeException,IllegalStateException,IllegalArgumentException,NullPointerException,他們能立馬知道每種異常的目的,而不是在代碼或文檔里查找用戶自定義異常的目的。
10)為方法拋出的異常編寫文檔
Java 提供了 throw 和 throws 關鍵字來拋出異常,在 javadoc 中可以用@throw 為任何可能被拋出的異常編寫文檔。如果你編寫 API 或者公共接口,這就變得非常重要。當任何方法拋出的異常都有相應的文檔記錄時,就能潛在的提醒任何調用該方法的開發者。
以上這些就是所有在 Java 異常處理需要遵循的最佳實踐。如果你有異常處理更好的最佳實踐,請務必讓我們知道。
- JVM
- 深入理解Java內存模型
- 深入理解Java內存模型(一)——基礎
- 深入理解Java內存模型(二)——重排序
- 深入理解Java內存模型(三)——順序一致性
- 深入理解Java內存模型(四)——volatile
- 深入理解Java內存模型(五)——鎖
- 深入理解Java內存模型(六)——final
- 深入理解Java內存模型(七)——總結
- Java內存模型
- Java內存模型2
- 堆內內存還是堆外內存?
- JVM內存配置詳解
- Java內存分配全面淺析
- 深入Java核心 Java內存分配原理精講
- jvm常量池
- JVM調優總結
- JVM調優總結(一)-- 一些概念
- JVM調優總結(二)-一些概念
- VM調優總結(三)-基本垃圾回收算法
- JVM調優總結(四)-垃圾回收面臨的問題
- JVM調優總結(五)-分代垃圾回收詳述1
- JVM調優總結(六)-分代垃圾回收詳述2
- JVM調優總結(七)-典型配置舉例1
- JVM調優總結(八)-典型配置舉例2
- JVM調優總結(九)-新一代的垃圾回收算法
- JVM調優總結(十)-調優方法
- 基礎
- Java 征途:行者的地圖
- Java程序員應該知道的10個面向對象理論
- Java泛型總結
- 序列化與反序列化
- 通過反編譯深入理解Java String及intern
- android 加固防止反編譯-重新打包
- volatile
- 正確使用 Volatile 變量
- 異常
- 深入理解java異常處理機制
- Java異常處理的10個最佳實踐
- Java異常處理手冊和最佳實踐
- Java提高篇——對象克隆(復制)
- Java中如何克隆集合——ArrayList和HashSet深拷貝
- Java中hashCode的作用
- Java提高篇之hashCode
- 常見正則表達式
- 類
- 理解java類加載器以及ClassLoader類
- 深入探討 Java 類加載器
- 類加載器的工作原理
- java反射
- 集合
- HashMap的工作原理
- ConcurrentHashMap之實現細節
- java.util.concurrent 之ConcurrentHashMap 源碼分析
- HashMap的實現原理和底層數據結構
- 線程
- 關于Java并發編程的總結和思考
- 40個Java多線程問題總結
- Java中的多線程你只要看這一篇就夠了
- Java多線程干貨系列(1):Java多線程基礎
- Java非阻塞算法簡介
- Java并發的四種風味:Thread、Executor、ForkJoin和Actor
- Java中不同的并發實現的性能比較
- JAVA CAS原理深度分析
- 多個線程之間共享數據的方式
- Java并發編程
- Java并發編程(1):可重入內置鎖
- Java并發編程(2):線程中斷(含代碼)
- Java并發編程(3):線程掛起、恢復與終止的正確方法(含代碼)
- Java并發編程(4):守護線程與線程阻塞的四種情況
- Java并發編程(5):volatile變量修飾符—意料之外的問題(含代碼)
- Java并發編程(6):Runnable和Thread實現多線程的區別(含代碼)
- Java并發編程(7):使用synchronized獲取互斥鎖的幾點說明
- Java并發編程(8):多線程環境中安全使用集合API(含代碼)
- Java并發編程(9):死鎖(含代碼)
- Java并發編程(10):使用wait/notify/notifyAll實現線程間通信的幾點重要說明
- java并發編程-II
- Java多線程基礎:進程和線程之由來
- Java并發編程:如何創建線程?
- Java并發編程:Thread類的使用
- Java并發編程:synchronized
- Java并發編程:Lock
- Java并發編程:volatile關鍵字解析
- Java并發編程:深入剖析ThreadLocal
- Java并發編程:CountDownLatch、CyclicBarrier和Semaphore
- Java并發編程:線程間協作的兩種方式:wait、notify、notifyAll和Condition
- Synchronized與Lock
- JVM底層又是如何實現synchronized的
- Java synchronized詳解
- synchronized 與 Lock 的那點事
- 深入研究 Java Synchronize 和 Lock 的區別與用法
- JAVA編程中的鎖機制詳解
- Java中的鎖
- TreadLocal
- 深入JDK源碼之ThreadLocal類
- 聊一聊ThreadLocal
- ThreadLocal
- ThreadLocal的內存泄露
- 多線程設計模式
- Java多線程編程中Future模式的詳解
- 原子操作(CAS)
- [譯]Java中Wait、Sleep和Yield方法的區別
- 線程池
- 如何合理地估算線程池大小?
- JAVA線程池中隊列與池大小的關系
- Java四種線程池的使用
- 深入理解Java之線程池
- java并發編程III
- Java 8并發工具包漫游指南
- 聊聊并發
- 聊聊并發(一)——深入分析Volatile的實現原理
- 聊聊并發(二)——Java SE1.6中的Synchronized
- 文件
- 網絡
- index
- 內存文章索引
- 基礎文章索引
- 線程文章索引
- 網絡文章索引
- IOC
- 設計模式文章索引
- 面試
- Java常量池詳解之一道比較蛋疼的面試題
- 近5年133個Java面試問題列表
- Java工程師成神之路
- Java字符串問題Top10
- 設計模式
- Java:單例模式的七種寫法
- Java 利用枚舉實現單例模式
- 常用jar
- HttpClient和HtmlUnit的比較總結
- IO
- NIO
- NIO入門
- 注解
- Java Annotation認知(包括框架圖、詳細介紹、示例說明)