Tips
書中的源代碼地址:https://github.com/jbloch/effective-java-3e-source-code
注意,書中的有些代碼裏方法是基於Java 9 API中的,因此JDK 最好下載 JDK 9以上的版本。java
許多Java程序員不喜歡檢查異常,但若是使用得當,他們能夠改進API和程序。 與返回碼和未檢查異常不一樣,它們迫使程序員處理異常問題,加強可靠性。 也就是說,在API中過分使用檢查異常會使它們使用起來不那麼使人愉快。 若是方法拋出檢查異常,則調用它的代碼必須在一個或多個catch塊中處理它們,或者聲明拋出它們並向上傳播。 不管哪一種方式,它都會給API的使用者帶來負擔。這種負擔在Java 8中加劇了,由於拋出檢查異常的方法不能直接在Stream中使用(條目45——48)。git
若是不能經過正確使用API來防止異常狀況,而且使用API的程序員在遇到異常時能夠採起一些有用的操做,那麼這種負擔是合理的。除非知足這兩個條件,不然可使用未檢查異常。做爲最後的檢驗(litmus test),能夠問問本身:程序員將如何處理異常。這是最好的辦法嗎?程序員
} catch (TheCheckedException e) { throw new AssertionError(); // Can't happen! }
或者這樣:github
} catch (TheCheckedException e) { e.printStackTrace(); // Oh well, we lose. System.exit(1); }
若是程序員不能作得更好,則須要一個未檢查異常。app
若是方法拋出的檢查異常是唯一的,那麼檢查異常給程序員帶來的額外負擔就會大得多。若是還有其餘方法,則該方法必須已經出如今try塊中,而且最多須要另外一個catch塊。若是一個方法拋出單個檢查異常,那麼這個異常就是該方法必須出如今try塊中,而且不能直接在Stream中使用。在這種狀況下,有必要問問本身是否有辦法避免檢查異常。性能
消除檢查異的最簡單方法是返回所需結果類型的Optional(條目 55)。該方法只返回一個空的Optional,而不是拋出一個檢查的異常。這種方法的缺點是,該方法不能返回任何詳細說明其沒法執行所需計算的額外信息。相反,異常具備描述性類型,而且能夠導出方法來提供額外的信息(條目 70)。測試
還能夠經過將拋出異常的方法分解爲兩個方法,將檢查異常轉換爲未檢查異常,第一個方法返回一個boolean值,表示是否拋出異常。 這個API重構將調用序列:線程
// Invocation with checked exception try { obj.action(args); } catch (TheCheckedException e) { ... // Handle exceptional condition }
轉換爲:code
// Invocation with state-testing method and unchecked exception if (obj.actionPermitted(args)) { obj.action(args); } else { ... // Handle exceptional condition }
這種重構並不老是合適的,可是它可使API更加溫馨。 雖而後者調用序列並不比前者更漂亮,但重構的API更靈活。 若是程序員知道調用將成功,或者知足於讓線程在失敗時終止,那麼重構也容許這個簡單的調用序列:對象
obj.action(args);
若是懷疑普通的調用序列會成爲常態,那麼API重構多是合適的。生成的API本質上與條目 69中的狀態測試方法API,而且適用與相同的警告:若是要在沒有外部同步的狀況下同時訪問對象,或者被外部轉換狀態,則此重構是不合適的,由於對象的狀態多是在對actionPermitted
和action
的調用之間進行更改。若是單獨的actionPermitted
方法會重複action
方法的工做,則可能會因性能緣由而排除重構。
總之,若是謹慎使用,檢查異常能夠提升程序的可靠性;當過分使用,會使API難以使用。若是調用者沒法從失敗中恢復,則拋出未檢查異常。若是恢復是可能的,而且但願強制調用者處理異常條件,那麼首先考慮返回Optional的。只有當在失敗的狀況下,沒法提供充分的信息時,才應該拋出一個檢查的異常。