原創做品,能夠轉載,可是請標註出處地址:http://www.cnblogs.com/V1haoge/p/7191280.htmlhtml
一、概述java
Java代碼中的異常處理是很是重要的一環,從代碼中能夠看到,它的使用已經和業務邏輯緊密的結合在一塊兒,部分業務邏輯仍是依靠異常來完成的,更多的時候進行異常處理能夠完善邏輯,避免可能的出錯,規避小錯誤引起的大停頓。算法
在通常的項目之中,都會自定義運行時異常,用以適應項目的須要,這種異常可被捕捉,也可不被捕捉,它們不會致使整個系統掛掉,可是不少狀況下,不捕捉處理就會致使業務出錯。spa
在這裏咱們模擬幾種狀況,點明異常捕捉的使用時機。指針
二、狀況分析code
先來看沒有任何處理的代碼htm
1 public class ExceptionTests01 { 2 3 public static void main(String[] args) { 4 System.out.println("---1---"); 5 invoke(); 6 System.out.println("---2---"); 7 8 } 9 10 public static void invoke(){ 11 System.out.println("---11---"); 12 int i = 1/0; 13 System.out.println("---12---"); 14 } 15 }
其執行結果以下:blog
---1---
---11---
Exception in thread "main" java.lang.ArithmeticException: / by zero
at com.donghao.test1.ExceptionTests01.invoke(ExceptionTests01.java:14)
at com.donghao.test1.ExceptionTests01.main(ExceptionTests01.java:7)
解析:main方法調用invoke方法,在執行到第12行時出錯,產生算法異常,此時因爲無任何異常處理手段,結果就是,程序執行到這裏以後直接中斷,執行結果中輸出的異常堆棧信息是Java內部默認的異常處理機制處理的結果。get
改造一:咱們在invoke方法內部加上異常捕捉機制,代碼以下:it
1 public class ExceptionTests01 { 2 3 public static void main(String[] args) { 4 System.out.println("---1---"); 5 invoke(); 6 System.out.println("---2---"); 7 8 } 9 10 public static void invoke(){ 11 try{ 12 System.out.println("---11---"); 13 int i = 1/0; 14 }catch(Exception e){ 15 System.out.println("---12---"); 16 } 17 System.out.println("---13---"); 18 } 19 }
執行結果:
---1--- ---11--- ---12--- ---13--- ---2---
結果解析:咱們在invoke方法的執行代碼外圍添加異常捕捉代碼,捕捉Exception異常,這是全部異常的基類,固然也包含這裏的算法異常,那麼這個捕捉機制就會將1/0產生的異常捕捉到,捕捉到這個異常以後,就會跳轉到catch語句塊中執行鍼對這個異常的處理語句,執行完成後,會繼續執行try...catch語句塊以後的代碼,這樣的好處顯而易見,一處的小錯誤並不會阻擋整個代碼的持續執行,固然若是是嚴重問題,咱們確實須要暫停執行的,就不能使用這種狀況,使用以前的代碼就行,因此異常處理機制的執行時機徹底是由項目的業務狀況而定的,是很是靈活的,不是固定的死板的。咱們要根據實際的業務場景來合理的使用纔是正理。
改造二:咱們在main方法中也添加異常捕捉
1 public class ExceptionTests01 { 2 3 public static void main(String[] args) { 4 try{ 5 System.out.println("---1---"); 6 invoke(); 7 }catch(Exception e){ 8 System.out.println("---2---"); 9 } 10 System.out.println("---3---"); 11 } 12 13 public static void invoke(){ 14 try{ 15 System.out.println("---11---"); 16 int i = 1/0; 17 }catch(Exception e){ 18 System.out.println("---12---"); 19 } 20 System.out.println("---13---"); 21 } 22 }
其執行結果以下:
---1--- ---11--- ---12--- ---13--- ---3---
結果幾乎與以前的徹底一致,不一樣之處在於2沒有輸出,一是我改變了2的輸出位置,並新增了3的輸出,如今3至關於以前2的位置,2沒有輸出的緣由是由於任何一個異常只能被捕捉一次,一旦被捕捉處理,那麼以後就不會再次被捕捉,即便我在main方法中將異常類型改爲算法異常,也不會捕捉到,異常只會被距離它最近的包含該異常的異常捕捉到,這裏的兩個異常捕捉其實就是一個嵌套的異常捕捉,並且兩者捕捉的異常仍是一致的,通常狀況咱們是不會這麼使用的,由於毫無心義。但不是說它就徹底不會出現,可能invoke中的代碼較長,會有多處異常狀況出現,咱們能夠在main方法中統一捕捉,而invoke中的異常捕捉只針對單一異常,表示這個異常的出現不會影響invoke方法後面的代碼執行,沒有異常捕捉的代碼一旦出現異常就會中斷其後方全部代碼的執行(同一代碼塊內),這個異常會被main方法中的異常捕捉機制捕捉到並執行處理,這樣main方法中調用invoke以後的代碼仍然能夠執行,不會被調用發生異常而中斷。
可是若是咱們再將invoke方法中的異常捕捉改變以下:
1 public class ExceptionTests01 { 2 3 public static void main(String[] args) { 4 try{ 5 System.out.println("---1---"); 6 invoke(); 7 }catch(Exception e){ 8 System.out.println("---2---"); 9 } 10 System.out.println("---3---"); 11 } 12 13 public static void invoke(){ 14 try{ 15 System.out.println("---11---"); 16 int i = 1/0; 17 }catch(NullPointerException e){ 18 System.out.println("---12---"); 19 } 20 System.out.println("---13---"); 21 } 22 }
執行結果發生了變化:
---1--- ---11--- ---2--- ---3---
爲何呢?正是由於咱們更改了invoke方法中捕捉的異常類型,以前是異常基類型Exception,如今改爲具體的空指針異常,那麼這個異常捕捉就只能捕捉空指針異常,它對此處發生的算法異常就會視而不見(因爲異常類型的不對口,那麼這個異常捕捉至關於沒有添加,能夠想象成沒有異常捕捉的狀況),這樣就致使invoke方法中在1/0發生異常以後的全部代碼所有不會執行,而咱們在main方法中新增的異常捕獲卻能捕獲到這種算法異常,因此12和13都不會輸出,而是在異常發生後直接就跳轉到main方法中進行異常捕捉,執行catch語句塊處理語句輸出2,而後是3。
在看一個特殊的狀況:
1 public class ExceptionTests01 { 2 3 public static void main(String[] args) { 4 System.out.println("---1---"); 5 invoke(); 6 System.out.println("---2---"); 7 } 8 9 public static void invoke(){ 10 for(int i = -2;i < 3;i++){ 11 System.out.println("---11---"); 12 System.out.println("12/"+i+"="+12/i); 13 System.out.println("---12---"); 14 } 15 System.out.println("---13---"); 16 } 17 }
invoke方法中是一個循環輸出,當第12行發生異常時,循環中斷,默認的異常處理機制打印異常堆棧:
---1---
---11---
12/-2=-6
---12---
---11---
12/-1=-12
---12---
---11---
Exception in thread "main" java.lang.ArithmeticException: / by zero
at com.donghao.test1.ExceptionTests01.invoke(ExceptionTests01.java:14)
at com.donghao.test1.ExceptionTests01.main(ExceptionTests01.java:7)
改造一:在invoke方法的for循環外部添加try...catch:
1 public class ExceptionTests01 { 2 3 public static void main(String[] args) { 4 System.out.println("---1---"); 5 invoke(); 6 System.out.println("---2---"); 7 } 8 9 public static void invoke(){ 10 try{ 11 for(int i = -2;i < 3;i++){ 12 System.out.println("---11---"); 13 System.out.println("12/"+i+"="+12/i); 14 System.out.println("---12---"); 15 } 16 System.out.println("---13---"); 17 }catch(Exception e){ 18 System.out.println("---14---"); 19 } 20 System.out.println("---15---"); 21 } 22 }
結果:
---1--- ---11--- 12/-2=-6 ---12--- ---11--- 12/-1=-12 ---12--- ---11--- ---14--- ---15--- ---2---
查看結果,發現循環仍是中斷了,當i=0時,第13行產生異常,以後循環中斷,而後異常纔會被for循環以外的異常捕捉到,這種場景也會在實際項目中出現,但很少見,具體場景爲,針對循環進行異常捕捉,一旦循環中某一環產生異常,則整個循環終止,處理異常。
改造二:在循環體中加入try...catch塊
1 public class ExceptionTests01 { 2 3 public static void main(String[] args) { 4 System.out.println("---1---"); 5 invoke(); 6 System.out.println("---2---"); 7 } 8 9 public static void invoke(){ 10 for(int i = -2;i < 3;i++){ 11 try{ 12 System.out.println("---11---"); 13 System.out.println("12/"+i+"="+12/i); 14 System.out.println("---12---"); 15 }catch(Exception e){ 16 System.out.println("---13---"); 17 } 18 System.out.println("---14---"); 19 } 20 System.out.println("---15---"); 21 } 22 }
執行結果:
---1--- ---11--- 12/-2=-6 ---12--- ---14--- ---11--- 12/-1=-12 ---12--- ---14--- ---11--- ---13--- ---14--- ---11--- 12/1=12 ---12--- ---14--- ---11--- 12/2=6 ---12--- ---14--- ---15--- ---2---
這種狀況比較多見,咱們將異常捕捉內置到for循環內部,只針對循環體進行異常捕捉,這樣當某一次循環體執行時產生了異常,也能私下處理好,不會影響整個循環的繼續執行。在循環中還能夠結合continue和break關鍵字進行更加複雜的關係控制,來達到特定的業務需求。
這裏我來展現一種狀況,也是剛剛作過的一個業務場景:
1 public class ExceptionTests01 { 2 3 public static void main(String[] args) { 4 System.out.println("---1---"); 5 invoke(); 6 System.out.println("---2---"); 7 } 8 9 public static void invoke(){ 10 for(int i = -2;i < 3;i++){ 11 try{ 12 System.out.println("---11---"); 13 System.out.println("12/"+i+"="+12/i); 14 System.out.println("---12---"); 15 }catch(Exception e){ 16 System.out.println("---13---"); 17 continue; 18 } 19 System.out.println("---14---"); 20 } 21 System.out.println("---15---"); 22 } 23 }
你沒看錯,只是添加了一個continue;控制信息的展現,當發生異常以後,執行catch塊代碼,輸出13後,再也不執行輸出14的操做,而是直接開始新的循環。
有必要作個總結:
1-異常的捕捉是有方向性和類型針對性的,異常會被距離異常發生點最近的包含或者就是捕捉該類型異常的捕捉點捕捉到。這樣咱們在作嵌套異常捕捉和多異常捕捉時,就必定要注意要將小範圍的異常類型放置到靠進try塊的位置,避免大類型劫持異常,致使你設置的異常類型沒法生效。
2-咱們將一段代碼try...catch包裹,就能夠將這段代碼從這個方法體中隔離出來,將其影響度降到最低,即便其發生異常,也不會影響到後續代碼的執行。
3-throw關鍵字的配合使用,咱們能夠在catch塊中使用,表示將捕捉到的異常再次拋出,這裏不作處理,這樣就必須在方法的調用處再次進行捕捉,能夠持續拋出,可是直到最終的方法時,必需要進行處理(對應明確拋出的異常必定要進行捕捉處理,不論你拋幾回)。
三、項目的異常處理
異常的轉換
項目中咱們都會自定義異常,這些異常通常帶有較爲明確的目的,甚至咱們可能會在項目中的每一層級定義不一樣的異常,這時候就會涉及到異常的轉換,其實轉換很簡單,只要將異常進行捕捉,在catch塊中將一行捕捉住,並從新拋出(throw)一個新的異常,將以前的異常信息e做爲新異常的參數。
1 try{ 2 int i = 1/0; 3 }catch(Exception e){ 4 throw new RuntimeException(e); 5 }
如上面的例子中,第2行會拋出一個異常,該異常將會被catch塊捕捉到,而後內部消化,從新拋出一個RuntimeException,並將原來的異常信息做爲新異常的異常信息(即保留原異常信息)。