異常
異常分爲錯誤(Error)與異常(Exception)
一、異常體系結構
java.lang.Throwable
- java.lang.Error:一般不編寫針對性的代碼進行處理
- java.lang.Exception:可以進行異常的處理
- 編譯時異常(checked)(也稱“受檢異常”)
- IOException
- ClassNotFoundException
- 運行時異常(unchecked)(也稱“非受檢異常”)
- NullPointerException
- ArrayIndexOutOfBoundsException
- ClassCastException
- NumberFormatException
- InputMismatchException
- ArithmeticException
- 編譯時異常(checked)(也稱“受檢異常”)
二、Java異常處理的方式
-
方式一:try-catch-finally
舉個例子:小孩在放羊,這時狼來了,小孩發現這個狼只是個狼崽子,他自己就有能力解決。羊就該喫草還喫草,相當於代碼正常執行
-
方式二:throws + 異常類型
這個就相當於來了一隻老狼,小孩處理不了了,需要在山下喊人,讓成人來解決,大人如果不能解決就會找警察。這種處理方式就相當於方式二
三、異常的處理:抓拋模型
-
過程一:“拋”:程序在正常執行的過程中,一旦出現了異常,就會在異常代碼處生成一個對應異常類的對象。並將此對象拋出。一旦拋出對象後,其後的代碼就不再執行。
關於異常對象的產生:① 系統自動生成的異常對象(在產生異常的代碼出生成一個異常對象)
② 手動的生成一個異常對象,並拋出(throw)
-
-
-
public class StudentTest { public static void main(String[] args) { Student s = new Student(); s.regist(-1001); } } class Student { private int id; public void regist(int id) { if(id > 0) { this.id = id; } else { //System.out.println("您輸入的數據非法!");//這裏如果輸入的數據非法,他還會返回一個id的默認值,還是會出來一個結果。按理說輸入非法應該報錯 //手動拋出一個異常對象,一般這個對象都是運行時異常,或編譯時異常。如果是編譯時異常就需要捕獲處理 //手動拋出運行時異常,編譯可以通過,可以不用處理 //throw new RunTimeException("您輸入的數據非法!"); //手動拋出編譯時異常,編譯都不會通過,這時就需要處理了 try { throw new Exception("您輸入的數據非法!");//手動拋出異常 } catch(Exception e) { System.out.println(e.getMessage()); } } } @Override public String toString() { return "Student " + "[id = " + id + "]"; } }
-
-
-
-
過程二:“抓”:可以理解爲異常的處理方式:① try-catch-finally ② throws
四、異常處理方式一:try-catch-finally 的使用
try {
//可能出現異常的代碼
}catch(異常類型1 變量名1){
//處理異常的方式1
}catch(異常類型2 變量名2){
//處理異常的方式2
}catch(異常類型3 變量名3){
//處理異常的方式3
}
...
finally{
//一定會執行的代碼
}
說明:
-
finally是可選的。
-
使用try將可能出現異常代碼包裝起來,在執行過程中,一旦出現異常,就會生成一個對應異常類的對象,根據此對象的類型,去catch中進行匹配
-
一旦try中的異常對象匹配到某個catch時,就進入catch中進行異常的處理。一旦處理完成,就跳出當前的try-catch結構(在沒有寫finally的情況)。繼續執行其後的代碼。
-
catch中的異常類型如果沒有子父類關係,則誰聲明在上,誰聲明在下無所謂。
catch中的異常類型如果滿足子父類關係,則要求子類一定聲明在父類的上面。否則,報錯
-
常用的異常對象的處理的方式:① getMessage()(返回值類型是String) ② printStackTrace()(void,無返回值,其拋出的異常信息包含getMessage()拋出的,所以比較常用)
-
在try結構中聲明的變量,在出了try結構以後,就不能再被調用
-
try-catch-finally結構可以嵌套。
體會1:使用try-catch-finally處理編譯時異常,使得程序在編譯時就不再報錯,但是運行時仍可能報錯。相當於我們使用try-catch-finally將一個編譯時可能出現的異常,延遲到運行時出現。
體會2:開發中,由於運行時異常比較常見,所以我們通常就不針對運行時異常編寫try-catch-finally了。
針對編譯時異常,我們說一定要考慮異常的處理。
五、finally
-
finally中聲明的是一定會被執行的代碼。即使catch中 出現異常了,try中有return語句,catch中有return語句等情況。
public void testMethod() { int num = method(); System.out.println(num); } public int method() { try{ int[] arr = new int[10]; System.out.println(arr[10]);//① return 1; }catch(ArrayIndexOutOfBoundsException e) { e.printStackTrace(); return 2; } finally { System.out.println("我一定會被執行!"); //return 3; } }
如果不註釋①號代碼,則輸出語句爲:我一定被執行!/n 2
如果註釋掉:輸出:我一定被執行!/n 1
如果給finally裏面加一個語句return 3; 則不管有沒有異常都輸出:輸出:我一定被執行!/n 3
來個情景對話方便理解:如果程序存在異常,那麼程序執行到catch代碼塊中。先執行e.printStackTrace();然後準備返回2,finally說等等,我還沒執行,我先執行。catch的return說,好,那你先執行,執行完了給我說一聲。然後finally就先執行第一句代碼,然後直接返回3,並沒有catch中return返回的機會。
不管是遇到了一個return還是製造的異常,都先執行finally的語句
-
像數據庫鏈接、輸入輸出流、網絡編程Socket等資源,JVM是不能自動的回收的,我們需要手動的進行資源的釋放。此時的資源釋放,就需要聲明在finally中。
六、異常處理方式二:throws + 異常類型
-
"throws + 異常類型"寫在方法的聲明處。指明此方法執行時,可能會拋出的異常類型。
一旦當方法體執行時,出現異常,仍會在異常代碼處生成一個異常類的對象,此對象滿足throws後的異常類型時,就會被拋出。異常代碼後續的代碼,就不再執行!
-
體會:try-catch-finally:真正的將異常處理掉了
throws的方式只是將異常拋給了方法的調用者。並沒有真正將異常處理掉。(最多拋到main方法裏,你就需要用try-catch-finally將他處理了)
歸根結底,最後都是需要用try-catch(-finally)來解決
-
在開發過程中如何選擇使用try-catch-finally還是throws?
- 如果父類中被重寫的方法沒有throws方式處理異常,則子類重寫的方法也不能使用throws,意味着如果子類重寫的方法中有異常,必須使用try-catch-finally方式處理。
- 執行的方法a中,先後又調用了另外的幾個方法,這幾個方法時遞進關係執行的(比如:方法三要用到方法二得到的結果,方法二又需要方法一得到的結果)。我們建議這幾個方法使用throws的方式進行處理。而執行的方法a可以考慮使用try-catch-finally方法進行處理。
七、如何自定義異常類?
-
繼承於先有的異常結構:RunTimeException、Excetion
-
提供全局常量:serialVersionUID
-
提供重載的構造器
public class MyException extends RunTimeException { static final long serialVersionUID = -4321532432453543543L; public MyException() { } public MyException(String msg) { super(msg); } }
八、throw和throws的區別:
throw表示拋出一個異常類的對象,生成異常對象的過程。聲明在方法體內
throws屬於異常處理的一種方式,聲明在方法的聲明處。