所謂的異常,就是程序按照正常執行邏輯走着走着遇到問題,崴了腳或閃了腰,已經沒法再繼續走下去了。怎麼辦呢?就像有病要治病同樣,有異常就得處理異常。Java提供了基本的語法來處理異常:一中是throw(s)語法,叫拋異常;一種是try-catch-finally語法,叫作捕獲異常。Java是面向對象的,在Java的世界裏一切皆是對象,因此異常天然也被當作是一種對象,當異常發生,就是建立了一個異常對象,咱們使用兩種語法來處理異常對象就好了,語法也很簡單,下面只簡單示例:程序員
public class Test { //繼續外拋異常 public void test1() throws FileNotFoundException { new FileInputStream("D://test.txt"); } //本身捕獲異常 public void test2() { try { new FileInputStream("D://test.txt"); } catch (FileNotFoundException e) { e.printStackTrace(); }finally{ //無論異常發沒發生都總會執行的部分,一般用來處理收尾工做,入內存清理,關閉流等等 } } //本身主動建立異常對象並拋出 public void test3() throws Exception { throw new Exception("拋出異常"); } }
這裏很基礎,仍是說三點:ide
① 最好從英文詞性上區分throw和throws兩個關鍵字。前者是動詞,表明拋出,很明顯是用在方法內部,執行異常對象的拋出動做;後者是名詞,用在方法後面,用來聲明方法執行可能拋出的一個或多個異常,多個異常之間用逗號隔開;測試
② finally塊不是必須的,除非業務中有必需要執行的部分;若是有finally塊,那該部分語句不管方法是否發生異常甚至中途執行return終止方法,該部分都會獲得執行;finally塊中不能使用return,會形成異常丟失;this
③ 若是你對異常是該拋出和本身處理舉棋不定,那布衣博主告訴你一個原則:該背鍋背鍋,不能背就甩鍋!是的,異常和平常中的甩鍋行爲相似。當別人向你甩來的鍋若是你能背,那就接住大膽背鍋處理異常,否則就甩鍋吧給上層調用者處理。編碼
打開JDK API 文檔,找到 lang包中 Throwable類,這就是Java異常的祖宗類了。你會發現異常家族體系至關龐大,但大而不亂,由於這祖宗也就兩個直接子類 Error 和 Exception。Error 一般指系統錯誤,程序員無需關心;Exception纔是須要拋出或者捕獲的異常的基類,其下有不少子類孫類,支系繁茂。對於Exception類型異常,又分了編譯期異常和運行時異常兩大類,前者是在編碼階段必須處理(捕獲或者拋出)的異常類型,不處理程序沒法正常編譯;後者不用主動處理,一般是程序邏輯上的異常,在程序運行時由虛擬機拋出。只需記住以下譜系圖便可:spa
Java標準類庫中的異常雖然已經不少了,可是那是標準庫中通用的異常狀況,不一樣項目會有不一樣的業務邏輯,這就須要咱們根據本身的業務邏輯來定義、處理本身的異常。最好的自定義方式是從跟本身業務處理意思最接近的異常類繼承,好比定義運行時異常能夠繼承RuntimeException,定義IO異常能夠繼承IOException。不過對異常情形限定得太窄其實也沒有必要了,直接繼承異常基類Exception便可:code
public class MyException extends Exception { public MyException() { super(); } public MyException(String message) { super(message); } public MyException(String message, Throwable cause) { super(message, cause); } }
這樣簡單的繼承,只是屏蔽了底層的具體異常類,對外暴露的是和本身業務相關的語義化的異常類名而已,功能依舊是父類異常的功能。這樣的自定義異常顯然功能太父類化了。更多的狀況下,咱們但願異常類能有比父類更完備的功能,以幫助程序向客戶端調者反饋更友好的信息。這個時候要跳出異常的字面意思去理解Java的異常機制,不能簡單的認爲異常就是發生了不想看到的錯誤,而是把異常當作一種控制流,是程序流程處理的一部分,控制着個人程序在狀況1 時走哪一步,在狀況2下又該執行哪一步。好比經過JNA調用底層動態庫獲取硬件信息,因爲程序運行的不可預見性,Java程序員和底層語言開發者之間會根據返回值約定一些異常情形,好比 0 表明什麼,1 表明什麼,諸如此類:對象
public void func(int result) throws MyException1 ,MyException2,MyException3{ switch (result){ case 1:throw new MyException1(); case 2:throw new MyException2(); case 3:throw new MyException3(); ... } }
像上面這種,利用窮舉的方式寫代碼確定是很不明智的,更多的時候,咱們會採用枚舉與自定義異常相結合的方式,來處理異常的流程控制,以更好的發揮自定義異常的自定義做用。blog
先定義約定的異常情形枚舉:繼承
//異常狀態枚舉 public enum ErrorCode { SUCCESS(0, "成功"), NOTFOUND(1, "未找到驗證設備"), READFAIL(2, "讀取驗證設備失敗"), OVERDUE(3, "驗證設備已過時"), UNKNOWN(4, "未知緣由的失敗"); int code; String msg; ErrorCode(int code, String msg) { this.code = code; this.msg = msg; } public int getCode() { return code; } public String getMsg() { return msg; } //根據狀態碼獲取狀態信息 public static ErrorCode getErrorCode(int code) { for (ErrorCode errorCode : values()) { if (errorCode.code == code) { return errorCode; } } return null; } @Override public String toString() { return "ErrorCode{" + "code=" + code + ", msg='" + msg + '\'' + '}'; } }
再定義本身的異常類:
public class LibException extends Exception { private int code; private ErrorCode errorCode; public ErrorCode getErrorCode() { return errorCode; } LibException(int code) { this.code = code; this.errorCode = ErrorCode.getErrorCode(code); } public int getCode() { return code; } public LibException(String message) { super(message); } public LibException(String message, Throwable cause) { super(message, cause); } }
經過枚舉和自定義異常的結合,便能在程序中動態的根據錯誤碼反饋給上層調用者友好的異常信息。測試:
public class Test { public static void chenbenbuyi(int result) throws LibException { if(result>=0&&result<=4)throw new LibException(result); System.out.println("result: "+result); } public static void main(String[] args) { try { chenbenbuyi(3); } catch (LibException e) { System.err.println("錯誤碼:"+e.getCode()+" 錯誤信息:"+e.getErrorCode().getMsg()); } } }
友好輸出反饋: