前言:說到異常體系,可能對於一些初入職場的老鐵會很頭痛,不可以很清晰的描述異常是個什麼狀況。那麼本文將經過打流水仗的方式給你們介紹一下工做中涉及的異常知識。首先能看到本文,說明也對異常是有了解的,因此文章開頭就經過一些概念和小例子快速熟悉一下異常,緊接着介紹異常體系,對於理解整個異常體系架構很是有幫助,最後介紹了很是很是重要的自定義異常,學會了這些,在工做中遇到的常見異常問題,處理起來必定都會遊刃有餘啦。
異常就是在程序的運行過程當中所發生的不正常的事件,它會中斷正在運行的程序java
Java 編程語言使用異常處理機制爲程序提供了錯誤處理的能力,好處就是:若是代碼中存在了異常,可是進行了捕獲處理,那麼程序就會繼續運行下去,不會由於一個異常致使程序中斷運行。數據庫
例子1:若是程序可能存在異常可是沒有作異常處理,那麼將會致使程序不能正常的運行下去:編程
public class ExceptionTest { public static void main(String[] args) { int a = 10; int b = 0; int c = a / b; System.out.println(c); System.out.println("輸出了本句話嗎"); } } 輸出結果是: Exception in thread "main" java.lang.ArithmeticException: / by zero at cn.caijiajia.cn.caijiajia.exception.ExceptionTest.main(ExceptionTest.java:12)
例子2:若是程序可能存在異常並作了異常處理,那麼程序就會正常的運行下去:架構
public class ExceptionTest { public static void main(String[] args) { try { int a = 10; int b = 0; int c = a / b; System.out.println(c); System.out.println("異常代碼下面的代碼將都不會執行"); } catch (ArithmeticException e) { System.out.println("處理異常的代碼"); } System.out.println("異常處理代碼下面的程序將會繼續執行而不會程序中斷"); } } 輸出結果是: Exception in thread "main" java.lang.ArithmeticException: / by zero at cn.caijiajia.cn.caijiajia.exception.ExceptionTest.main(ExceptionTest.java:12) 處理異常的代碼 異常處理代碼下面的程序將會繼續執行而不會程序中斷
try:執行可能產生異常的代碼
catch:捕獲異常並對異常狀況作相應處理
finally:不管是否發生異常,代碼總能執行。(釋放資源,關閉數據庫鏈接)
throws:聲明可能拋出的各類異常(受檢異常較多)
throw:手動的拋出異常(手動拋出咱們自定義的異常較多)編程語言
狀況一:沒產生異常學習
public void method() { try { // 代碼段① [正常業務邏輯代碼,此處不會產生異常] } catch (Exception e) { // 代碼段② [對異常處理的代碼段] } // 代碼段③ [正常業務邏輯代碼] } 運行結果: 代碼段① 代碼段③
狀況二:產生異常並捕獲異常spa
public void method() { try { // 異常代碼段① [正常業務邏輯代碼,此處會產生異常] // 代碼段② [正常業務邏輯代碼] } catch (Exception e) { // 代碼段③ [對異常處理的代碼段] } // 代碼段④ [正常業務邏輯代碼] } 運行結果: 代碼段① 代碼段③ 代碼段④ 注意:若是想要正常的 代碼段② 執行,那麼能夠把代碼段從 trycatch 裏面提出來,和 代碼段④ 放在一塊兒,當異常處理完以後,就能夠同時去執行 代碼段② 和 代碼段④ 了。
狀況三:產生異常並捕獲異常,可是捕獲異常類型不匹配產生異常類型指針
public void method() { try { // 異常代碼段① [正常業務邏輯代碼,此處會產生角標越界異常] // 代碼段② [正常業務邏輯代碼] } catch (IOException e) { // 代碼段③ [對異常處理的代碼段] } // 代碼段④ [正常業務邏輯代碼] } 運行結果: 代碼段① 注意:若是捕獲異常類型和產生的異常類型不匹配,那麼就和沒有處理異常狀況同樣了,trycatch 後面的代碼段將都不會執行,發生異常就會致使程序中斷運行
Error 類和 Exception 類的父類都是 Throwable 類code
Error 類通常是指與虛擬機相關的問題,如系統崩潰,虛擬機錯誤,內存空間不足,方法調用棧溢等。對於這類錯誤的致使的應用程序中斷,僅靠程序自己沒法恢復和和預防,遇到這樣的錯誤,建議讓程序終止。所以咱們在學習的時候主要是學會 Exception。對象
Exception 類表示程序能夠處理的異常,能夠捕獲且可能恢復。遇到這類異常,應該儘量處理異常,使程序恢復運行,而不該該隨意終止異常。
Exception 異常主要分爲兩大類:Checked Exception,Unchecked Exception;即受檢異常和非受檢異常(運行時異常)
Checked Exception:受檢異常,即 Java 程序必須顯式處理的異常,若是程序沒有處理 Checked 異常,該程序在編譯時就會發生錯誤沒法編譯。例如圖中所示的 IOException
處理受檢異常經過有以下兩種方式:
方式一:經過 trycatch 顯式處理異常,不然編譯不經過
public class ExceptionTest { public static void main(String[] args) { try { // 經過 trycatch 顯示處理異常 FileInputStream fis = new FileInputStream(new File("")); } catch (IOException e) { e.printStackTrace(); } } }
方式二:經過 throws 拋出異常,讓上層來處理異常,不然編譯不經過
public class ExceptionTest { public static void main(String[] args) throws IOException { FileInputStream fis = new FileInputStream(new File("")); } }
Unchecked Exception:非受檢異常,即 RuntimeException 運行時異常。這些異常程序能夠選擇捕獲處理,也能夠不處理。這些異常通常是由程序邏輯錯誤引發的,程序應該從邏輯角度儘量避免這類異常的發生。好比常見的 NullPointerException、IndexOutOfBoundsException 就是運行時異常。不過按照經驗來講,這類異常要麼儘可能避免,要麼出現了就要作異常處理,從而保證程序的健壯性。
處理運行時異常經過有以下兩種方式:
方式一:常見的空指針處理
// 經過寫僞代碼來演示其處理流程 if (對象 == null) { // 處理對象爲 null 的邏輯 } else { // 處理對象不爲 null 的邏輯 }
方式二:跟業務相關異常,拋出自定義異常
// 經過手機號註冊業務邏輯 User user = dao.getByPhone(phone); if (user != null) { // 說明此手機號碼已經被註冊,那麼就拋出業務異常(即自定義異常) throw new MyRuntimeException("改用戶已註冊"); } // 若是沒有註冊,繼續走註冊流程代碼
總結:經過本小節異常分類及處理能夠發現,受檢異常處理起來很簡單,由於程序若是不作顯式處理,那麼就會編譯不經過,強制要求處理;而運行時異常則是看心情處理的,可是若是想要公司代碼更加健壯,更少的出現問題,最好要作一下異常處理;可是若是作這個處理呢?對於這種運行時異常,大部分都是和業務相關的,好比手機號註冊例子;這種狀況下在 Java 的異常體系中並無相關異常類作處理,由於 Java 無論在智能,也不可能知道咱們的業務狀況,固然就不會針對業務提供一些異常類供咱們使用,所以爲了解決這個問題,自定義異常就出現了,它對於咱們處理業務中產生的運行時異常很是很是重要,接下來就來學習自定義異常。
Java 現有的異常類不能知足更多的業務異常處理,所以咱們要自定義合適的異常類來處理業務異常。
第一步:聲明一個類繼承 Exception 或其子類
那麼咱們聲明的這個類到底繼承誰呢?Exception?RuntimeException?
答案是:RuntimeException
緣由是:看了上面的異常類層次圖,應該也能發現,Exception 下面有兩大類子類,受檢異常和運行時異常,若是咱們自定義的類繼承了 Exception,則就會由於受檢異常的存在而變成了受檢異常類,這個時候咱們自定義的異常類,若是在程序中使用,那麼就必須顯式處理異常,要麼 trycatch,要麼拋出給上層;這樣一來,使得咱們的程序很混亂,並且並無達到咱們預期的結果。而後當咱們自定義的類繼承了 RuntimeException 以後,當咱們程序中想要使用的時候,直接 new 一個便可,而再也不須要顯式去再多作處理了。
第二步:自定義異常類應至少包含四個構造方法
public class MyException extends RuntimeException { public MyException() {} public MyException(String msg) { super(msg); } public MyException(Throwable throwable) { super(throwable); } public MyException(String msg, Throwable throwable) { super(msg, throwable); } }
第三步:在程序中使用咱們的自定義異常
public void testException() { User user = dao.getByPhone(phone); if (user != null) { // 由於 MyException 是繼承 RuntimeException,因此這裏直接拋出異常而不用作其它處理 throw new MyException("該手機號已被註冊"); } // ... } 注:沒錯,這裏又使用了這個手機號註冊的例子,由於這個就是實實在在的在業務中的異常處理。業務是變幻無窮,可是它們可能產生的異常處理方式是不會變化的,按照這個思路去作異常處理便可。
tips:如上自定義的異常類中的構造方法是最基本的幾個。每家公司的自定義異常可能都會有區別,好比定義一個 SuperException 類實現了 RuntimeException,而後在自定義 ClientException,ServerException 再去繼承 SuperException;那麼這就是一套自定義體系了,分爲客戶端異常和服務端異常,在須要作異常處理的地方使用對應的異常類並拋出異常錯誤信息便可了。再好比可能有的公司會在自定義異常類中在定義一些字段,code,msg 等,來表明某些業務碼和對應的錯誤信息等。無論怎麼樣,咱們只要瞭解自定義異常的原理後,面對哪一個公司的自定義異常體系咱們都可以輕鬆應對。
經過本文的學習,必定要掌握兩個知識,一個是異常體系,要分清受檢異常和運行時異常;另外一個就是自定義異常,知道如何自定義異常和如何使用自定義異常。