首先,思考這樣一個問題,爲何要使用異常?我對這個問題思考了不少遍,也看到一些答案,可是始終不理解,下面會經過一些實例來探索這個問題的答案。java
異常,表示意外狀況。異常是在方法中拋出的,具體來講,異常是在方法的前置條件不知足的狀況下拋出的。例如,有一個方法public static Connection getConnection(String url) throws SQLException能夠獲取一個數據庫鏈接,須要一個url參數,當方法(API)的調用者在調用這個方法的時候傳遞進來的url參數爲空或者不合法,那麼就違反了這個方法的前置條件。mysql
如今來考慮若是沒有異常程序的流程怎樣執行。無論這個方法是這樣實現的,由於不知足依賴,方法確定不能完成它的功能。若是沒有異常,那麼API的調用者應該怎樣處理呢?算法
Connection con = getConnection(url); if(con != null) {正常流程} else{錯誤處理}
Java異常設計者認爲這樣的處理方式讓業務和錯誤處理的代碼耦合在一塊兒了,這不是一個好的方式。另外並非全部的API調用者都會恰當的處理,好比不執行判斷,直接使用可能形成程序直接退出,這樣是徹底沒有恢復機會的,而且沒有辦法獲得錯誤信息。sql
如今再一次來看使用異常的緣由:數據庫
1. 讓業務處理代碼和錯誤處理代碼解耦工具
2. 提供給API調用者相關的錯誤信息url
3. 給API調用者相關的恢復機會spa
第一個緣由就不說了,我的有我的的見解。設計
第二個緣由,我的認爲這是Java異常最棒的地方,Java異常提供的堆棧信息很是方便幫助定位錯誤,有利於程序的調試。多是由於習慣了Java異常給咱們的堆棧信息,咱們才認爲是理所固然的事情,試着使用一下那些異常定義使用很差的第三方jar包就知道調試沒有異常的程序是一種怎樣的感覺了。調試
在說第三個緣由以前,先說一個重要的概念,受檢異常與非受檢異常,非受檢異常是隻直接或間接繼承自RuntimeException類的異常類。其餘的異常屬於受檢異常。
圖1 受檢異常與非受檢異常
受檢異常能夠理解爲是要受到編譯器檢查的異常,檢查什麼呢?檢查異常有沒有被處理,意思就是受檢異常必須被捕獲或者申明拋出。受檢異常是但願API調用者從異常中恢復。
import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; public class WhyUseException { private static final String DEFAULT_ALGORITHM = "MD5"; public static void main(String[] args) { @SuppressWarnings("unused") MessageDigest md = null; md = getMessageDigest("sha-256"); } public static MessageDigest getMessageDigest(String algorithm) { MessageDigest md = null; try { return MessageDigest.getInstance(algorithm); } catch (NoSuchAlgorithmException e) { e.printStackTrace(); try { md = MessageDigest.getInstance(DEFAULT_ALGORITHM); } catch (NoSuchAlgorithmException e1) { // e1.printStackTrace(); // 使用默認的md5,肯定是存在的,因此斷言不會執行到這個地方 } } return md; } }
如上面程序所示的NoSuchAlgorithmException就是一個受檢異常,由於NoSuchAlgorithmException繼承自GeneralSecurityException,而GeneralSecurityException繼承自Exception,不是直接或者間接的繼承自RuntimeException,因此NoSuchAlgorithmException是一個受檢異常。
對於受檢異常是必需要什麼拋出,或者捕獲的,這是受檢異常設置的意圖,但願從異常中恢復。就像上面程序所示的同樣當拋出NoSuchAlgorithmException異常時,咱們的恢復方法就是使用默認的md5算法。可是大多數時候咱們只是但願獲得錯誤信息,而不是從錯誤中恢復。這樣形成的麻煩是很是多的,由於受檢異常必須捕獲或者什麼拋出,因此就會有不少try catch或者throws申明這樣的不是很優雅的代碼。這對於API的調用者也絕對是一種負擔。想Matin Flower和Think in Java的做者都認爲受檢異常的麻煩比好處多。
我的認爲Java的受檢異常的確不夠優雅,這主要是最開始寫數據鏈接的代碼的時候一直強迫我捕獲異常,而且那時候我並不知道爲何,也不知道怎樣優雅的處理。不過受檢異常存在的確是有用的。
import java.sql.Connection; import java.sql.DriverManager; import java.sql.SQLException; import java.sql.Statement; public class ExceptionTest { public static void main(String[] args) { for(int i =0;i<10;i++) testValidConnection(); while(true);//不讓程序結束,能夠在數據庫中看到數據庫鏈接並無關閉 } public static boolean testValidConnection() { Connection connection = null; try { Class.forName("com.mysql.jdbc.Driver"); connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/test", "tim", "123456"); Statement statement = connection.createStatement(); statement.executeQuery("select *");//錯誤的SQL語句,拋出異常 connection.close();//並無執行到 } catch (ClassNotFoundException e) { e.printStackTrace(); } catch (SQLException e) { e.printStackTrace(); }finally{ // if(connection != null) // try { // connection.close(); // } catch (SQLException e) { // // ignore // } } return false; } }
如上面的程序所示,SQLException也是一個受檢異常,在上面的程序中咱們沒有作任何的處理,咱們就把當作非受檢異常同樣沒有捕獲異常。咱們只是調用了這個方法10次,以下圖所示,使用MySql客戶端工具執行show processlist能夠看到每一次調用都會創建一個鏈接而且沒有關閉。這樣早晚會形成資源耗盡的狀況。
圖2 數據庫鏈接
因此對於異常的使用建議是儘可能不要使用受檢異常,除非特別強調須要API調用者必須執行一些清理工做。對於異常的其餘最佳實踐推薦《effective java》下面是一張關於異常的知識的一些整理。
圖3 異常知識點整理