java exception

一, 什麼是java裏的異常

 

因爲java是c\c++ 發展而來的,  首先咱們先看看c語言裏的錯誤.java

 

1.1 c語言裏的錯誤

        咱們實現1個程序的過程包括,  代碼編寫, 編譯代碼成爲程序,  執行程序.c++

.

 

        其中大部分常見的語法錯誤都會被編譯代碼這樣部過濾掉.   可是即便經過了編譯. 執行程序這一步可能仍是會有錯誤.程序員

 

        緣由不少, 例如常見的除數爲0,    內存溢出(數組的index超出界限), 或者內存被其餘程序修改等.面試

 

        最簡單的例子:編程

[java] view plain copy數組

 

  1. #include <stdio.h>  
  2.   
  3. int f(int a, int b){  
  4.     return a/b;  
  5. }     
  6.   
  7. int main(){  
  8.     int i = f(8,0);  
  9.         printf("i is %d\n",i);  
  10.     return 0;     
  11. }  


 


       上面的例子編譯時是無錯的,  可是一旦執行就會提示吐核錯誤了. 安全

 

       c語言裏對這種執行時出現的錯誤是無能爲力的,  一旦出錯就會整個程序崩潰, 就不會在繼續執行下面的代碼.jvm

 

       並且不少時候出錯信息不多, 讓你沒法判斷出錯的緣由和地方, 只能一步步當心debug...函數

 

       因此不少用c寫的程序有時會出現非法關閉的現象.spa

 

       解決方法只能是在代碼裏對可能出錯的地方添加if 判斷.

 

       例如f()函數裏能夠對b進行判斷, 若是是0就不執行.

 

 

1.2 java裏運行時出現的錯誤

       java裏編譯器對代碼的規範性比c嚴格得多. 可是即便如此,  經過編譯的java程序有時也很難避免執行時出錯.

 

       例如, 將上面的c程序改編成java程序:

 

[java] view plain copy

 

  1. package Exception_kng;  
  2.   
  3. class Exp1{  
  4.     public int f(int a, int b){  
  5.         return a/b;  
  6.     }  
  7. }  
  8.   
  9. public class Expt_1{  
  10.     public static void g(){  
  11.         Exp1 e = new Exp1();  
  12.         int i = e.f(8,0);  
  13.         System.out.printf("i is %d\n", i);  
  14.     }  
  15. }  

 

      運行時同樣會出錯, 下面是出錯信息:

 

[java] view plain copy

 

  1. [java] Caused by: java.lang.ArithmeticException: / by zero  
  2. [java]  at Exception_kng.Exp1.f(Expt_1.java:5)  
  3. [java]  at Exception_kng.Expt_1.g(Expt_1.java:12)  
  4. [java]  at Enter_1.main(Enter_1.java:31)  

 

          可是能夠見到, java告訴你出錯的類型: 運算錯誤(ArithmeticExcetion), 出錯信息和出錯的類與文件行數輸出, 方便你調試.  jvm虛擬機是會對錯誤做出必定的處理的.

  

          因此能夠簡單地將java裏的異常理解成java運行時出現的錯誤,  異常機制就是對這種錯誤進行處理的機制.

 

 

1.3 java異常的定義

        實際上, 當java程序執行時出現錯誤時, jvm會把執行時出錯的信息(例如出錯緣由, 類型, 位置) 收集,而後打包成爲1個對象(object),  程序員能夠對這種對象進行處理. 這種對象就是所謂的異常.

 

        可能出現的異常的代碼並非確定會出現異常, 取決於執行環境和數據.!

 

二, java裏的異常的分類.

 

見下圖:

 

                       Throwable

                      /                \

              Error             Exception

                 /                   /               \

         xxxxxx             xxxxxx          RuntimeException

                                                          /                   \

                                                     xxxxxx             ArithmeticException

 

 

 

上圖的全部對象都是類.     

 

Throwable 表明是可拋出的.

Error            表明的是嚴重錯誤,  這種錯誤程序員沒法進行處理, 例如操做系統崩潰, jvm出錯, 動態連接庫失敗等.  Error並非異常, 不是本文的重點.

 

Exception    表明的就是異常了.  它下面不少派生類,   其中它的派生類也分兩種, 一種是RuntimeException(運行時異常), 其餘的都是非運行時異常

 

RuntimeException    包括除數爲0, 數組下標超界等. 運行時異常的派生類有不少, 其產生頻率較高.  它的派生類能夠由程序處理或者拋給(throw) 給jvm處理. 例如上面的例子就是拋給了jvm處理, jvm把程序中斷執行, 並把錯誤信息輸出到終端上.

 

非RuntimeExcption   這種異常屬於Excepion的派生類(上面紅色的xxx), 可是不是RuntimeException的派生類,  這種異常必須由程序員手動處理,不然不經過編譯.

 

ArithmeticExcpetion   算術異常, 它是RuntimeException的派生類, 因此程序員不手動處理也經過編譯, 只不過出錯時會被jvm處理.

 

 

 

 

三, java裏對異常的處理

java裏對異常的處理有三種.

3.1 程序猿對有可能出現的異常使用try catch處理.

例如咱們將上面的例子改動一下:

 

[java] view plain copy

 

  1. package Exception_kng;  
  2.   
  3. class Exp2{  
  4.     public int f(int a, int b){  
  5.         int i = 0;  
  6.         try{  
  7.             i = a/b;  
  8.         }  
  9.         catch(Exception e){  
  10.             System.out.printf("Exception occurs!!\n");  
  11.             System.out.println(e.getMessage());  //print the root cause  
  12.             System.out.printf("===========================\n");  
  13.             e.printStackTrace(); //print the info of function stuck.  
  14.         }  
  15.   
  16.         return i;  
  17.     }  
  18. }  
  19.   
  20. public class Expt_2{  
  21.     public static void g(){  
  22.         Exp2 ex = new Exp2();  
  23.         int i = ex.f(8,0); //call f()  
  24.         System.out.printf("i is %d\n", i);  //successfully executed  
  25.     }  
  26. }  


 

 

在f()函數中對可能出現的異常的代碼進行try catch處理後,  程序會執行catch裏的代碼. 並且不會中斷整個程序, 繼續執行try catch後面的代碼.

 

程序執行輸出:

 

[java] view plain copy

 

  1. [java] Exception occurs!!  
  2. [java] / by zero  
  3. [java] ===========================  
  4. [java] java.lang.ArithmeticException: / by zero  
  5. [java]  at Exception_kng.Exp2.f(Expt_2.java:7)  
  6. [java]  at Exception_kng.Expt_2.g(Expt_2.java:23)  
  7. [java]  at Enter_1.main(Enter_1.java:31)  
  8. [java] i is 0  


 

注意最終也執行了g()函數中的最後一條語句, 輸出了i的值.

 

也就是說try catch處理後並不會終止程序, 令程序即便出現了錯誤,  也能夠對錯誤進行必定的處理後繼續執行. 這就是java異常機制比c語言安全的地方.

 

下面會詳細講解 try catch.

 

注:

 

getMessage() 方法:   Exception類的方法之一,  返回異常的緣由,   上面的 / by zero 就是這個方法輸出的.

 

printStackTrace():      Exception類的方法之一,  在屏幕輸出函數棧信息,  也就是異常出現的地方.

 

 

3.2 函數裏並不處理異常, 使用throw or throws 關鍵字 把可能出現的異常拋給調用該函數的上級函數處理.

 

例如我在f()函數中不想處理可能出現的異常,  想把它拋出上級函數處理:

 

下面是個例子:

 

[java] view plain copy

 

  1. package Exception_kng;  
  2.   
  3. class Exp3{  
  4.     public int f(int a, int b){  
  5.         if (0 == b){  
  6.             throw new ArithmeticException("Shit !!! / by zero!");  
  7.               
  8.         }   
  9.   
  10.         return a/b;  
  11.     }  
  12. }  
  13.   
  14. public class Expt_3{  
  15.     public static void g() throws ArithmeticException{  
  16.         Exp3 ex = new Exp3();  
  17.         int i = 22;  
  18.         i = ex.f(8,0); //throw excetpion   
  19.         System.out.printf("i is %d\n", i);  //failed executed   
  20.         System.out.printf("g() is done!!\n");  //failed executed  
  21.     }  
  22.   
  23.     public static void h(){  
  24.         try{  
  25.             g();      
  26.         }catch(ArithmeticException e){  
  27.             System.out.printf("Exception occurs!!\n");  
  28.             System.out.println(e.getMessage());  //print the root cause  
  29.             System.out.printf("===========================\n");  
  30.             e.printStackTrace(); //print the info of function stuck.  
  31.         }  
  32.   
  33.         System.out.printf("h() is done!!\n");  //successfully executed  
  34.     }  
  35. }  

能夠見到f() 加了個條件判斷, 若是參數b = 0, 使用throw 直接手動拋出1個異常. 讓調用它的函數處理.

 

g()調用f()函數, 預見到f()可能有異常, 可是也不想處理,  使用throws 關鍵字告訴調用它的函數本函數有可能拋出這種異常. // 注, 這裏的throws對程序並無實質的影響.

 

h()調用g(),  簡單g()定義的throws, 用try catch在本函數進行處理.

 

 

輸出:

[java] view plain copy

 

  1. [java] Exception occurs!!  
  2. [java] Shit !!! / by zero!  
  3. [java] ===========================  
  4. [java] java.lang.ArithmeticException: Shit !!! / by zero!  
  5. [java]  at Exception_kng.Exp3.f(Expt_3.java:6)  
  6. [java]  at Exception_kng.Expt_3.g(Expt_3.java:18)  
  7. [java]  at Exception_kng.Expt_3.h(Expt_3.java:25)  
  8. [java]  at Enter_1.main(Enter_1.java:31)  
  9. [java] h() is done!!  


注意這個程序沒有執行g() 最後的代碼.

 

 

throw 和 throws 後面也會詳細講解.

 

 

3.3 交給jvm虛擬機處理

 

假如上面的例子h() 也不處理怎麼辦?  就如1.2 的例子, 會拋給jvm處理.

 

可是這種狀況只適用於RuntimeExecption及其派生類.

 

jvm怎麼處理呢,  就是中斷整個程序, 並把異常信息輸出到屏幕上.

實際上, 當java程序的1個函數拋出異常時,  
首先會檢查當前函數有沒有try catch處理, 若是無檢查上一級函數有無try..catch處理....
這樣在函數棧裏一級一級向上檢查, 若是直至main函數都無try..catch, 則拋給jvm..

 

項目中強烈建議儘可能手動處理, 不要把異常交給jvm.

 

 

四,Try catch finally 的處理機制.

這裏開始詳解try catch finally了.

 

語法是這樣的.

 

try{

       可能出異常的若干行代碼;

}

catch(ExceptionName1 e){

       產生ExceptionName 1的處理代碼;

}

catch(ExceptionName2 e){

       產生ExceptionName 2的處理代碼;

}

...

finally{

      不管如何, 最終確定會執行的代碼

}

 

 

4.1 try catch finally的執行路線.

下面用個例子來講明:

[java] view plain copy

 

  1. try{  
  2.     f();  
  3.     ff();  
  4. }  
  5. catch(ArithmeticException e){  
  6.     g();  
  7. }  
  8. catch(IOException e){  
  9.     gg();  
  10. }  
  11. catch(AuthorizedException e){  
  12.     ggg();  
  13. }  
  14. finally{  
  15.     h();  
  16. }  
  17.   
  18. k();  

 

4.1.1 當try裏面的f()拋出了IOException

當f()拋出了異常, 那麼ff()就不會執行了.  程序會嘗試捕捉異常.

首先捕捉ArithmeticException,  捕捉失敗.

接下來捕捉IOException, 捕捉成功, 執行gg();

一旦捕捉到一個異常, 不會再嘗試捕捉其餘異常, 直接執行finally裏的h();

執行後面的函數k().

也就是說路線是:

f()  ->  gg()  -> h() -> k()


有2點要注意的.

1. f()函數極有可能未完整執行, 由於它拋出了異常, 拋出異常的語句執行失敗, 以後的語句放棄執行.

2. try{} 裏面, f()以後的語句, 例如ff()放棄執行.

 

4.1.2 沒有任何異常拋出

這種狀況很簡單, 就是try{}裏面的代碼被完整執行, 由於沒有拋出任何異常, 就不會嘗試執行catch裏的部分, 直接到finally部分了.

路線是:
f()  ->  ff()  -> h() -> k()

 

 

4.2 如何肯定要捕捉的異常名字.

 

也許有人會問, 咱們怎麼知道到底會拋出什麼異常?

下面有3個解決方案.

1.看代碼憑經驗, 例如看到1段除法的代碼, 則有可能拋出算術異常.

2.在catch的括號裏寫上Exception e,  畢竟Exception 是全部其餘異常的超類, 這裏涉及多態的知識,  至於什麼是多態能夠看看本人的另外一篇文章.

3. 觀察被調用函數的函數定義, 若是有throws後綴, 則能夠嘗試捕捉throws 後綴拋出的異常

 

4.3 爲何須要finally

 

包括我在內不少人會以爲finally語句簡直多勾餘, 既然是否捕捉到異常都會執行, 上面那個例子裏的h()爲何不跟下面的k() 寫在一塊兒呢.

上面的例子的確看不出區別.

 

但下面兩種狀況下就體現了finally獨特的重要性.

 

4.3.1 拋出了1個異常, 可是沒有被任何catch子句捕捉成功.

 

例如try裏面拋出了1個A異常,  可是隻有後面只有捕捉B異常, 和C異常的子句. 

這種狀況下, 程序直接執行finally{}裏的子句,  而後中斷當前函數, 把異常拋給上一級函數, 因此當前函數finally後面的語句不會被執行.

例子:

 

[java] view plain copy

 

  1. package Exception_kng;  
  2.   
  3. import java.net.*;  
  4. import java.io.*;  
  5.   
  6. class Exp4{  
  7.     public int f(int a, int b) throws IOException, BindException{  
  8.         return a/b;  
  9.     }  
  10. }  
  11.   
  12. public class Expt_4{  
  13.     public static void g(){  
  14.         Exp4 ex = new Exp4();  
  15.         int i = 22;  
  16.         try{  
  17.             System.out.printf("g() : try!!\n");  //failed  
  18.             i = ex.f(8,0); //call f()    
  19.         }  
  20.         catch(BindException e){  
  21.             System.out.printf("g() : BindException!!\n");  //failed  
  22.         }  
  23.         catch(IOException e){  
  24.             System.out.printf("g() : IOException!!\n");  //failed  
  25.         }  
  26.         finally{  
  27.             System.out.printf("g() : finaly!!\n");  //successfully executed  
  28.         }  
  29.         System.out.printf("g() is done!!\n");  //failed  
  30.     }  
  31.   
  32.     public static void h(){  
  33.         try{  
  34.             g();      
  35.         }catch(ArithmeticException e){  
  36.             System.out.printf("Exception occurs!!\n");  
  37.             System.out.println(e.getMessage());  //print the root cause  
  38.             System.out.printf("===========================\n");  
  39.             e.printStackTrace(); //print the info of function stuck.  
  40.         }  
  41.   
  42.         System.out.printf("h() is done!!\n");  //successfully executed  
  43.     }  
  44. }  


我所說的狀況, 就在上面例子裏的g()函數,  g()函數裏嘗試捕捉兩個異常, 可是拋出了第3個異常(ArithmeticException 算術異常).

 

因此這個異常會中斷g()的執行, 由於沒有被捕捉到, 而後拋給調用g()的 h()函數處理,  而在h()捕捉到了, 因此h()函數是能完整執行的.

也就是說g()裏的

 

[java] view plain copy

 

  1. System.out.printf("g() is done!!\n");  //failed  

 

執行失敗

而h()裏的

 

[java] view plain copy

 

  1. System.out.printf("h() is done!!\n");  //successfully executed  


執行成功

 

可是不管如何, g()裏的finally{}部分仍是被執行了

執行結果以下:

 

[java] view plain copy

 

  1. [java] g() : try!!  
  2. [java] g() : finaly!!  
  3. [java] Exception occurs!!  
  4. [java] / by zero  
  5. [java] ===========================  
  6. [java] java.lang.ArithmeticException: / by zero  
  7. [java]  at Exception_kng.Exp4.f(Expt_4.java:8)  
  8. [java]  at Exception_kng.Expt_4.g(Expt_4.java:18)  
  9. [java]  at Exception_kng.Expt_4.h(Expt_4.java:34)  
  10. [java]  at Enter_1.main(Enter_1.java:31)  
  11. [java] h() is done!!  

 

 

這種狀況是1中編程的低級錯誤, 在項目中是不容許出現.

避免方法也十分簡單,  在catch子句集的最後增長1個catch(Exception e)就ok, 由於Exception是全部異常的超類, 只要有異常拋出, 則確定會捕捉到.

 

 

4.3.2 在catch子句內有return子句.

 

下面例子:

 

[java] view plain copy

 

  1. try{  
  2.     f();  
  3.     ff();  
  4. }  
  5. catch(ArithException e){  
  6.     g();  
  7.     return j();  
  8. }  
  9. catch(IOException e){  
  10.     gg();  
  11.     return j();  
  12. }  
  13. catch(AuthorizedException e){  
  14.     ggg();  
  15.     return j();  
  16. }  
  17. finally{  
  18.     h();  
  19. }  
  20.   
  21. k();  


假如在f()函數拋出了IOExcepion 異常被捕捉到.

 

 

那麼執行路線就是

f()  ->  gg()  -> j() -> h() -> 上一級function

也就說, 這種狀況下finally裏的子句會在return回上一級function前執行. 然後面的k()就被放棄了.

 

 

4.3.3 finally做用小結.

 

能夠看出, finally裏的語句, 不管如何都會被執行.

至有兩種狀況除外, 一是斷電, 二是exit函數.

 

在項目中, 咱們通常在finally編寫一些釋放資源的動做, 例如初始化公共變量. 關閉connections, 關閉文件等.

 

4.4 try catch finally裏一些要注意的問題.

 

4.4.1 不管如何最多隻有1個catch被執行

 

這個上面提到過了, 一旦捕捉到1個異常, 就不會嘗試捕捉其餘異常.

若是try裏面的一段代碼可能拋出3種異常A B C,  

首先看它先拋出哪一個異常, 若是先拋出A,  若是捕捉到A, 那麼就執行catch(A)裏的代碼. 而後finally.. B和C就沒有機會再拋出了.

若是捕捉不到A, 就執行finally{}裏的語句後中斷當前函數, 拋給上一級函數...(應該避免)

 

 

4.4.2 有可能全部catch都沒有被執行

 

兩種狀況, 1就是沒有異常拋出, 另外一種就是拋出了異常可是沒有捕捉不到(應該避免)

4.4.3 先捕捉子類異常, 再捕捉父類異常, 不然編譯失敗

加入try 裏面嘗試捕捉兩個異常, 1個是A, 1個是B, 可是A是B的父類.

這種狀況下, 應該把catch(B)寫在catch(A)前面.

緣由也很簡單, 加入把catch(A)寫在前面, 由於多態的存在, 即便拋出了B異常, 也會被catch(A)捕捉, 後面的catch(B)就沒有意義了.

也就是說若是捕捉Exception這個異常基類, 應該放在最後的catch裏,  項目中也強烈建議這麼作, 能夠避免上述4.3.1的狀況出現.

 

 

4.4.4 catch與catch之間不能有任何代碼.

 

這個沒什麼好說的. 語法規則

 

 

4.4.5 finally裏不能訪問catch裏捕捉的異常對象e

 

每1個異常對象只能由catch它的catch子句裏訪問.

 

 

4.4.6 try裏面的定義變量不能在try外面使用.

 

跟if相似, 很少說了.

 

 

4.4.7 try catch finally能夠嵌套使用.

 

這個也不難理解..

 

 

 

五, throw 和throws的機制和用法.

 

下面開始詳講異常另外一種處理方法throw 和 throws了.

注意的是, 這兩種用法都沒有真正的處理異常, 真正處理的異常方法只有try catch, 這兩種方法只是交給上一級方法處理.

 

就如一個組織裏 , 有1個大佬, 1個黨主, 1個小弟.

大佬叫黨主幹活, 堂主叫小弟幹活,  而後小弟碰上麻煩了,   可是小弟不會處理這個麻煩, 只能中斷工做拋給黨主處理, 而後堂主發現這個麻煩只有大佬能處理, 而後拋給大佬處理..

道理是相通的..

 

5.1 throw 的語法與做用

 

throws的語法很簡單.

語法:

throw new XException();

其中xException必須是Exception的派生類.

這裏注意throw 出的是1個異常對象, 因此new不能省略

 

做用就是手動令程序拋出1個異常對象.

 

 

 

5.2 throw 1個 RuntimeException及其派生類

咱們看回上面3.2 的例子:

 

 

[java] view plain copy

 

  1. public int f(int a, int b){  
  2.         if (0 == b){  
  3.             throw new ArithmeticException("Shit !!! / by zero!");  
  4.   
  5.         }   
  6.   
  7.         return a/b;  
  8. }  

 

 

 

5.2.1 throw會中斷當前函數, 當前函數執行失敗(不完整)

當這個函數的if 判斷了b=0時, 就利用throws手動拋出了1個異常.  這個異常會中斷這個函數. 也就是說f()執行不完整, 是沒有返回值的.

 

 

 

 

5.2.2, 接下來哪一個調用這個函數就會在調用這個函數的語句上收到異常.

[java] view plain copy

 

  1. public void g(){  
  2.     int i;  
  3.     h();  
  4.     i = f(); //recevie excepton  
  5.     k();  
  6. }  

 

例如上沒的g()函數, 在調用f() 會收到1個異常.

這時g()函數有三種選擇.

 

1. 不作任何處理

這時, g()收到f()裏拋出的異常就會打斷g()執行, 也就是說g()裏面的k(); 被放棄了, 而後程序會繼續把這個函數拋給調用g()函數.

 

而後一級一級尋求處理, 若是都不處理, 則拋給jvm處理.  jvm會中斷程序, 輸出異常信息. 這個上沒提到過了.

 

2. 使用try catch處理

若是catch成功, 則g()函數能完整執行, 並且這個異常不會繼續向上拋.

若是catch失敗(儘可能避免), 則跟狀況1相同.

 

 

 

 

5.3 throw 1個 非RuntimeException派生類的異常

將上面的例子改一下:

 

 

[java] view plain copy

 

  1. public int f(int a, int b){  
  2.         if (0 == b){  
  3.             throw new IOException("Shit !!! / by zero!");  
  4.         }   
  5.   
  6.         return a/b;  
  7.     }  


例如, 我不想拋出ArithmeticException,  我想拋出IOExcetpion.

 

注意 這裏, IOException雖然邏輯上是錯誤的(徹底不是IO的問題嘛), 可是在程序中徹底可行, 由於程序猿能夠根據須要控制程序指定拋出任何1個異常.

 

可是這段代碼編譯失敗, 由於IOException 不是 RuntimeException的派生類.

 

java規定:

 

 

 

 

5.3.1 若是一個方法裏利用throw手動拋出1個非RuntimeException異常, 必須在函數定義聲明裏加上throws 後綴

 

 

 

改爲這樣就正確了:

 

[java] view plain copy

 

  1. public int f(int a, int b) throws IOException{  
  2.         if (0 == b){  
  3.             throw new IOException("Shit !!! / by zero!");  
  4.         }   
  5.   
  6.         return a/b;  
  7. }  


注意在方法定義里加上了throws子句.  告訴調用它的函數我可能拋出這個異常.

 

 

 

 

5.3.2 調用該方法的方法則必須處理這個異常

 

例如抄回上面的例子, g()調用f()函數.

 

[java] view plain copy

 

  1. public void g(){  
  2.     int i;  
  3.     h();  
  4.     i = f(); //recevie excepton  
  5.     k()  
  6. }  


可是編譯失敗.

 

 

由於f()利用throws 聲明瞭會拋出1個非runtimeExcetpion.  這時g()必須作出處理.

處理方法有兩種:  

 

1. try catch本身處理:

 

[java] view plain copy

 

  1. public void g(){  
  2.     int i = 0;  
  3.     h();  
  4.     try{  
  5.         i = f(); //recevie excepton  
  6.     }  
  7.     catch(IOException e){  
  8.   
  9.     }  
  10.     k();  
  11. }  


須要注意的是, catch裏面要麼寫上throws對應的異常(這裏是 IOException), 要麼寫上這個異常的超類, 不然仍是編譯失敗.

 

 

2.g()利用throws 往上一級方法拋

.

[java] view plain copy

 

  1. public void g() throws IOException{  
  2.     int i = 0;  
  3.     h();  
  4.     i = f(); //recevie excepton  
  5.     k();  
  6. }  

 

這是調用g()的函數也要考慮上面的這兩種處理方法了... 

可是最終上級的方法(main 方法)仍是不處理的話, 就編譯失敗, 上面說過了, 非runtimeException沒法拋給jvm處理.

 

雖然這兩種處理方法都能經過編譯, 可是運行效果是徹底不一樣的.

第一種, g()能完整執行.

第二種, g()被中斷, 也就是g()裏面的k(); 執行失敗.

 

 

5.4 throws 的語法.

 

throws稍微比throw難理解點:

語法是:

public void f() throws Exception1, Exception2...{

 

}

也就是講, thorws能夠加上多個異常, 注意這裏拋出的不是對象, 不能加上new.

並且不是告訴別人這個函數有可能拋出這麼多個異常. 而是告訴別人, 有可能拋出這些異常的其中一種.

 

 

5.5 throws 的做用.

 

若是爲f()函數加上throws後續, 則告訴調用f()的方法, f()函數有可能拋出這些異常的一種.

 

若是f()throws 了1個或若干個非RuntimeException,  則調用f()的函數必須處理這些非RuntimeException, 如上面的g()函數同樣.

若是f() throws的都是RuntimeException, 則調用f()的函數能夠不處理, 也能經過編譯, 可是實際上仍是強烈建議處理它們.

 

實際上, 若是1個方法f() throws A,B

那麼它有可能不拋出任何異常.(程序運行狀態良好)

也有能拋出C異常(應該避免, 最好在throws上加上C)

 

 

5.6 何時應該用throws

 

 

5.6.1 一個函數體裏面手動throw了1個RumtimeException, 則這個函數的定義必須加上throws子句

 

這個是強制, 告訴別人這個函數內有炸彈.

 

 

5.6.2 一個函數內有可能由系統拋出異常.

 

這個是非強制的, 可是若是你知道一個函數內的代碼有可能拋出異常, 最好仍是寫上throws 後綴

不管這個異常是否runtimeExcepion.

 

 

5.7 通常狀況下,調用1個帶有throws方法時怎麼辦

 

我的建議, 若是你調用1個函數throws A, B, C

那麼你就在當前函數寫上

try

catch(A)
catch(B)

catch(C)

catch(Exception)

 

 

這樣能處理能保證你的函數能完整執行, 不會被收到的異常中斷.

 

固然若是你容許你的函數能夠被中斷, 那麼就能夠在當前函數定義加上throws A, B 繼續拋給上一級的函數.

 

 

5.8 重寫方法時, throws的範圍不能大於超類的對應方法.

 

例如你在一個派生類重寫一個方法f(), 在超類裏的f() throws A, B   你重寫方法時就不throws出 A,,B,C 或者throws A和B的超類.

 

緣由也是因爲多態的存在.

由於1個超類的引用能夠指向1個派生類的對象並調用不一樣的方法.  若是派生類throws的範圍加大

 

那麼利用多態寫的代碼的try catch就再也不適用.  

 

六, throw和throws一些主要區別.

面試問得多,單獨拉出來寫了:

 

 

 

6.1 throw 寫在函數體內, throws寫在函數定義語句中.

 

應付面試官.

 

 

6.2 throw 是拋出1個異常對象, throws是有能拋出異常的種類

 

因此throw後面的通常加上new 和exception名字().

而throws後面不能加上new的

 

 

6.3 一個方法最多隻能throw1個異常, 可是能夠throws多個種類異常

 

由於一旦一個函數throw出1個異常, 這個函數就會被中斷執行, 後面的代碼被放棄, 若是你嘗試在函數內寫兩個throw, 編譯失敗.

 

而throws 是告訴別人這個函數有可能拋出這幾種異常的一種. 可是最多隻會拋出一種.

 

 

6.4 若是在一個函數體內throw 1個非runtimeException, 那麼必須在函數定義上加上throws後綴.  但反過來就不是必須的.

 

緣由上面講過了. 

 

 

七, 自定義異常.

 

咱們能夠自定義異常, 只須要編寫1個類, 繼承1個異常類就ok

例子:

 

[java] view plain copy

 

  1. package Exception_kng;  
  2.   
  3. class User_Exception1 extends ArithmeticException{  
  4.     public User_Exception1(String Exception_name){  
  5.         super(Exception_name);  
  6.     }  
  7.   
  8.     public void printStackTrace(){ //overwrite  
  9.         super.printStackTrace();  
  10.         System.out.printf("hey man, i am an user_defined excetpion\n");    
  11.     }  
  12. }  
  13.   
  14. class Exp6{  
  15.     public int f(int a, int b){  
  16.         if (0 == b){  
  17.             throw new User_Exception1("Shit !!! / by zero!"); //use User_defined exception  
  18.         }   
  19.   
  20.         return a/b;  
  21.     }  
  22. }  
  23.   
  24. public class Expt_6{  
  25.     public static void g() {  
  26.         Exp6 ex = new Exp6();  
  27.         int i = 22;  
  28.         try{  
  29.             i = ex.f(8,0); //throw excetpion   
  30.         }catch(User_Exception1 e){  
  31.             e.printStackTrace();  
  32.         }  
  33.         System.out.printf("i is %d\n", i);   
  34.         System.out.printf("g() is done!!\n");   
  35.     }  
  36. }  

 

 

上面的類User_Exception1 就是1個自定義異常, 並重寫了printStackTrace()方法.

 

 

 

 

八,java異常的優缺點.

8.1 c語言是如何處理程序錯誤的.

 

咱們要理解異常的優缺點, 首先看看沒有異常的C語言是如何處理錯誤的.

下面是個例子:

 

[cpp] view plain copy

 

  1. //openfile  
  2. if (fileOpen() > 0){  
  3.     //check the length of the file  
  4.     if (gotLengthOfTheFile() > 0){  
  5.         //check the memory  
  6.         if (gotEnoughMemory() > 0){  
  7.             //load file to memory  
  8.             if (loadFileToMem() > 0){  
  9.                 readFile();  
  10.             }else{  
  11.                 errorCode = -5;  
  12.             }  
  13.   
  14.         }else{  
  15.             errorCode = -5;  
  16.         }  
  17.   
  18.     }else{  
  19.         errorCode = -5;  
  20.     }  
  21.   
  22. }else{  
  23.     errorCode = -5;  
  24. }  
  25.   
  26. //handle error  
  27. case errorCode....  
  28.   
  29. //release Source  
  30. releaseSource();  

 

 

能夠見到c語言處理錯誤有這些特色

1. 大部分精力都在錯誤處理.

2. 須要把各類可能出現的錯誤所有考慮到, 才能保證程序的穩定性.

3. 程序可讀性差, 錯誤處理代碼混雜在其餘代碼中.

4. 出錯返回信息少, 一旦出錯難以調試.

5. 一旦出現了未考慮到的錯誤, 資源釋放代碼沒法執行.

 

 

 

8.2 java異常機制下是如何編寫上述代碼的.

 

 

[java] view plain copy

 

  1. try{  
  2.     fileOpen();  
  3.     gotLengthOfTheFile();  
  4.     gotEnoughMemory();  
  5.     loadFileToMem();  
  6.     readFile();  
  7. }  
  8. catch(fileOpenFail) {  handle1()}  
  9. catch(gotLengthOfTheFileFail) {  handle2()}  
  10. catch(gotEnoughMemoryFail) {  handle3()}  
  11. catch(loadFileToMemFail) {  handle4()}  
  12. catch(readFileFail) {  handle4()}  
  13. catch(Exception e)   {  handle5()} //catch unexpected error  
  14. finally{  
  15.     releasSource();   
  16. }  
相關文章
相關標籤/搜索