您現在的位置是:首頁 > 動作武俠首頁動作武俠

JavaSE系列】Java異常處理機制

簡介}}}可以呼叫異常類中的 p r i n t S t a c k T r a c e ( ) 方 法 printStackTrace()方法 p r i n t S t a c k T ra c e ( ) 方 法 列印異常錯誤資訊

所有異常類的父類是什麼

本篇文章帶大家認識Java基礎知識——異常,在理想的情況下,使用者輸入的資料格式永遠是正確的,一個程式也許可能永遠沒有bug,但是在實際情況當中,這種理想的情況非常少,使用者輸入資料格式出現錯誤是非常常見的,需要一種機制解決類似的問題,而在Java中引入了異常的概念,本篇部落格將圍繞什麼是異常,異常怎麼解決兩個維度對異常做一個詳細地分析。

本文由

未見花聞

原創,

CSDN

首發!

參考書籍:《Java核心技術》,《Java程式設計思想》,《Effective Java》

參考線上程式設計網站: 牛客網 力扣

作者水平很有限,如果發現錯誤,一定要及時告知作者哦!感謝感謝!

導航小助手

JavaSE系列】Java異常處理機制

1。初見Java異常

1。1熟悉的面孔

在Java的學習過程中,一定遇到過除數為0,陣列越界,對空引用操作等,這些情況可以透過編譯,但是在執行的時候編譯器會報出異常,這些異常就是Java異常機制中的一部分。

System。out。println(10 / 0);//除數為0

JavaSE系列】Java異常處理機制

int[] arr = new int[10];System。out。println(arr[11]);

JavaSE系列】Java異常處理機制

String str = null;System。out。println(str。length());

JavaSE系列】Java異常處理機制

A r i t h m e t i c E x c e p t i o n , A r r a y I n d e x O u t O f B o u n d s E x c e p t i o n , N u l l P o i n t e r E x c e p t i o n ArithmeticException,ArrayIndexOutOfBoundsException,NullPointerException A r i t h m e t i c E x c e p t i o n , A r r a y I n d e x O u t O f B o u n d s E x c ep t i o n , N u l l P o i n t e r E x c e p t i o n 都是初學Java時常遇到的一部分異常,但是Java異常機制包括的不僅僅只有異常,還有錯誤也是屬於Java異常機制範圍內的。

JavaSE系列】Java異常處理機制

1。2防禦式程式設計

錯誤在程式碼中是客觀存在的。 因此我們要讓程式出現問題的時候及時通知程式猿, 我們有兩種主要的方式:

LBYL: Look Before You Leap。 在操作之前就做充分的檢查。

EAFP: It’s Easier to Ask Forgiveness than Permission。 “事後獲取原諒比事前獲取許可更容易”。 也就是先操作, 遇到問題再處理。

打個比方,有一天你心血來潮,看到一位非常漂亮的小姐姐,你很想牽她的手,但是你非常的有禮貌,先詢問這位小姐姐:我能牽你的手嗎?,這位小姐姐毫不猶豫地說:不行!,這一種情形就是LBYL方式;與此同時,另一位比較強悍的兄弟也想牽這位小姐姐的手,於是直接拿起這位小姐姐的手,牽了起來,然後這位小兄弟被扇了一巴掌,這種情形就是EAFP方式。

Java異常核心思想就是EAFP。

1。3異常的優點

使用異常能夠更好地處理業務,程式碼也更有條理,更加清晰明白,不使用異常程式碼正常的程式碼與可能異常的程式碼混在一起,比較混亂。

比如一個模擬實現一個遊戲開始的過程:

不使用異常:

public class Test2 { public static void main(String[] args) { boolean ret = false;//賬號密碼匹配結果 ret = 密碼驗證結果(); if (!ret) { //處理遊戲登入失敗業務 } ret = 載入遊戲結果(); if (!ret) { //處理載入遊戲失敗業務 } ret = 聯機匹配結果(); if (!ret) { //處理聯機匹配失敗業務 } ret = 氪金是否成功結果(); if (!ret) { //處理氪金失敗業務 } }}

使用異常:

public class Test2 { public static void main(String[] args) { try { 密碼驗證(); 載入遊戲(); 聯機匹配(); 氪金(); }catch (密碼驗證異常) { //處理密碼錯誤異常 }catch (載入遊戲異常) { //處理載入遊戲異常 }catch (聯機匹配異常) { //處理聯機匹配異常 }catch (氪金異常) { //處理氪金異常 } }}

2。異常處理

2。1捕獲異常

想要捕獲一個異常,前提需要知道可能會發生什麼型別的異常,最常見的異常就是前面所舉例的 A r i t h m e t i c E x c e p t i o n , A r r a y I n d e x O u t O f B o u n d s E x c e p t i o n , N u l l P o i n t e r E x c e p t i o n ArithmeticException,ArrayIndexOutOfBoundsException,NullPointerException A r i t h m e t i c E x c e p t i o n , A r r a y I n d e x O u t O f B o u n d s E x c ep t i o n , N u l l P o i n t e r E x c e p t i o n 分別為除數為0異常,陣列越界異常,空指標異常。

當然,Java異常體系中的異常種類非常多,Exception類下的直接子類就有:

JavaSE系列】Java異常處理機制

RuntimeException類是目前我們接觸最多的,前面列舉的幾個常見的異常都是該類的子類。

JavaSE系列】Java異常處理機制

2。1。1捕獲一個異常

我們在進行除法計算時,可能會出現除數為0,訪問陣列物件時,可能出現數組越界,使用一個物件時,可能會出現空指標異常,知道一段程式碼可能出現某種或某些異常,我們可以對異常進行捕獲,語法規則為:

try { //可能出現異常的程式碼段}catch (異常類 異常變數名) { //處理該異常}

try

程式碼塊中放的是可能出現異常的程式碼。

catch

程式碼塊中放的是出現異常後的處理行為。

public class Test3 { public static void main(String[] args) { int a = 0; try { System。out。println(8 / a); }catch (ArithmeticException e) { System。out。println(“捕捉到了除數為0異常!”); } int[] arr = new int[10]; try { System。out。println(arr[20]); }catch (ArrayIndexOutOfBoundsException e) { System。out。println(“捕捉到了一個數組越界異常!”); } String str = null; try { System。out。println(str。equals(“aaa”)); }catch (NullPointerException e) { System。out。println(“捕捉到了一個空指標異常!”); } }}

JavaSE系列】Java異常處理機制

可以呼叫異常類中的 p r i n t S t a c k T r a c e ( ) 方 法 printStackTrace()方法 p r i n t S t a c k T ra c e ( ) 方 法 列印異常錯誤資訊:

public class Test3 { public static void main(String[] args) { int a = 0; try { System。out。println(8 / a); }catch (ArithmeticException e) { e。printStackTrace(); System。out。println(“捕捉到了除數為0異常!”); } int[] arr = new int[10]; try { System。out。println(arr[20]); }catch (ArrayIndexOutOfBoundsException e) { e。printStackTrace(); System。out。println(“捕捉到了一個數組越界異常!”); } String str = null; try { System。out。println(str。equals(“aaa”)); }catch (NullPointerException e) { e。printStackTrace(); System。out。println(“捕捉到了一個空指標異常!”); } }}

JavaSE系列】Java異常處理機制

如果異常沒有被主動捕捉(使用try…catch語句捕捉),這個異常會交給JVM處理,一旦交給JVM處理,發生異常,將非正常終止程式,後續程式碼不會執行。

public class Test6 { public static void main(String[] args) { int[] arr = new int[10]; System。out。println(arr[20]);//發生陣列越界異常,該異常沒有被捕獲,將交給JVM處理 System。out。println(“我是異常程式碼的後續程式碼,我被執行了!”); }}

JavaSE系列】Java異常處理機制

程式碼非正常退出,異常處後的程式碼不會被執行!

主動捕獲異常,異常處後面的程式碼會執行,並且程式正常退出:

public class Test5 { public static void main(String[] args) { int[] arr = new int[10]; try { System。out。println(arr[20]); }catch (ArrayIndexOutOfBoundsException e) { e。printStackTrace(); System。out。println(“捕捉到了一個數組越界異常!”); } System。out。println(“我是異常程式碼的後續程式碼,我被執行了!”); }}

JavaSE系列】Java異常處理機制

2。1。2捕獲多種異常

Java異常機制支援多種異常的捕獲,最常用的方法,就是使用多個catch語句,捕捉多個異常的具體型別。

一個try…catch語句,catch語句可以存在多個:

public class Test4 { public static void main(String[] args) { try { //可能出現異常的程式碼段 }catch (異常類 異常變數名) { //處理該異常 }catch (。。。) { //處理 }。。。 }}

而對於try語句中的程式碼段,當代碼段中遇到異常時,發生異常的後面的所有程式碼段將不再執行,會直接執行對應catch中的語句。

比如,我將三句都有異常的程式碼寫在同一個try語句中,使用多個catch語句捕捉這三種類型的異常,實際上只會捕捉到第一個異常。

public class Test7 { public static void main(String[] args) { int a = 0; int[] arr = new int[10]; String str = null; try { System。out。println(8 / a); System。out。println(arr[20]); System。out。println(str。equals(“aaa”)); } catch (ArithmeticException e) { e。printStackTrace(); System。out。println(“捕捉到了除數為0異常!”); } catch (ArrayIndexOutOfBoundsException e) { e。printStackTrace(); System。out。println(“捕捉到了一個數組越界異常!”); } catch (NullPointerException e) { e。printStackTrace(); System。out。println(“捕捉到了一個空指標異常!”); } }}

JavaSE系列】Java異常處理機制

try語句中第一個異常語句為除數為0而引發的算術異常,所以最終捕捉的異常只有算術異常。

如果第一個語句沒有異常,則捕捉的是後面第一個發生異常語句的異常,也就是說一個try語句只能捕獲一個異常。

public class Test7 { public static void main(String[] args) { int a = 2; int[] arr = new int[10]; String str = null; try { System。out。println(8 / a); System。out。println(arr[20]); System。out。println(str。equals(“aaa”)); } catch (ArithmeticException e) { e。printStackTrace(); System。out。println(“捕捉到了除數為0異常!”); } catch (ArrayIndexOutOfBoundsException e) { e。printStackTrace(); System。out。println(“捕捉到了一個數組越界異常!”); } catch (NullPointerException e) { e。printStackTrace(); System。out。println(“捕捉到了一個空指標異常!”); } }}

JavaSE系列】Java異常處理機制

除了使用多個catch語句,還可以使用

|

分隔符在一個catch語句中定義多種型別的異常類:

public class Test8 { public static void main(String[] args) { int[] arr = new int[10]; String str = null; try { System。out。println(2/0); } catch (ArrayIndexOutOfBoundsException | NullPointerException | ArithmeticException e) { e。printStackTrace(); System。out。println(“捕捉到了一個除數為0異常或陣列越界異常或空指標異常!”); } }}

JavaSE系列】Java異常處理機制

public class Test8 { public static void main(String[] args) { int[] arr = new int[10]; String str = null; try { System。out。println(arr[20]); System。out。println(str。equals(“aaa”)); } catch (ArrayIndexOutOfBoundsException | NullPointerException | ArithmeticException e) { e。printStackTrace(); System。out。println(“捕捉到了一個除數為0異常或陣列越界異常或空指標異常!”); } }}

JavaSE系列】Java異常處理機制

public class Test8 { public static void main(String[] args) { int[] arr = new int[10]; String str = null; try { System。out。println(str。equals(“aaa”)); } catch (ArrayIndexOutOfBoundsException | NullPointerException | ArithmeticException e) { e。printStackTrace(); System。out。println(“捕捉到了一個除數為0異常或陣列越界異常或空指標異常!”); } }}

JavaSE系列】Java異常處理機制

也可以直接使用這些類的父類

RuntimeExcept

或父類的父類

Exception

處理其所有子類型別的異常,但是不推薦,因為範圍太廣了,如果沒有 p r i n t S t a c k T r a c e ( ) 方 法 printStackTrace()方法 p r i n t S t a c k T r a c e ( ) 方 法 ,根本不知道程式發生了什麼異常。

public class Test8 { public static void main(String[] args) { int[] arr = new int[10]; String str = null; try { System。out。println(str。equals(“aaa”)); } catch (RuntimeException e) { e。printStackTrace(); System。out。println(“捕捉到了一個執行時異常!”); } }}

JavaSE系列】Java異常處理機制

最後,還有一點需要注意,就是使用多個catch語句時,子類異常要在父類異常前面,否則編譯器會報錯!這一點其實很好理解,正所謂“小的打不過,老的再來!”。

JavaSE系列】Java異常處理機制

我把

NullPointerException

定義在其父類

RuntimeException

後面,編譯器報錯了,我換一下編譯器就沒有報錯了,程式也能夠正常執行。

JavaSE系列】Java異常處理機制

JavaSE系列】Java異常處理機制

所以,

catch

語句捕獲異常時,捕獲異常的順序應該是:

子 類 − > 父 類 子類 -> 父類 子 類 − > 父 類

2。2finally

try。。。catch

語句的基礎之上,後面還可以加上

finally

語句,無論是否發生異常,異常是否被捕獲,該語句裡面的程式碼塊都會被執行,一般用於資源的關閉,比如常見的

Scanner

類,使用該類進行輸入是需要進行資源關閉的。語法規則:

try { //可能出現異常的程式碼段}catch (異常類 異常變數名) { //處理該異常}catch (。。。) { //處理}finally { //處理,不論異常是否捕獲,異常是否交給JVM處理,該語句都會執行}

finally

程式碼塊中的程式碼用於處理善後工作, 會在最後執行。

import java。util。InputMismatchException;import java。util。Scanner;public class Test10 { public static void main(String[] args) { Scanner sc = new Scanner(System。in); try { int a = sc。nextInt(); System。out。println(10 / a); } catch (InputMismatchException e) { e。printStackTrace(); System。out。println(“輸入資料型別錯誤!”); } catch (ArithmeticException e) { e。printStackTrace(); System。out。println(“算術異常,除數為0!”); } sc。close();//關閉資源 }}

JavaSE系列】Java異常處理機制

因為

sc。close()

語句不論什麼情況都要執行,所以該程式碼可以改成:

import java。util。InputMismatchException;import java。util。Scanner;public class Test11 { public static void main(String[] args) { Scanner sc = new Scanner(System。in); try { int a = sc。nextInt(); System。out。println(10 / a); } catch (InputMismatchException e) { e。printStackTrace(); System。out。println(“輸入資料型別錯誤!”); } catch (ArithmeticException e) { e。printStackTrace(); System。out。println(“算術異常,除數為0!”); } finally { sc。close();//關閉資源 } }}

JavaSE系列】Java異常處理機制

剛才的程式碼可以有一種等價寫法, 將 Scanner 物件在 try 的 ( ) 中建立, 就能保證在 try 執行完畢後自動呼叫 Scanner的 close 方法,上面的程式碼也等價於:

import java。util。InputMismatchException;import java。util。Scanner;public class Test11 { public static void main(String[] args) { try (Scanner sc = new Scanner(System。in)) { int a = sc。nextInt(); System。out。println(10 / a); } catch (InputMismatchException e) { e。printStackTrace(); System。out。println(“輸入資料型別錯誤!”); } catch (ArithmeticException e) { e。printStackTrace(); System。out。println(“算術異常,除數為0!”); } }}

使用finally的弊端:

public class Test12 { public static int func() { try { return 10; }finally { return 20; } } public static void main(String[] args) { System。out。println(func()); }}

JavaSE系列】Java異常處理機制

所以使用

finally

時,要避免在

finally

語句中進行返回值返回。

2。3Java異常體系

Java 內建了豐富的異常體系, 用來表示不同情況下的異常。

JavaSE系列】Java異常處理機制

頂層類

Throwable

派生出兩個重要的子類,

Error

Exception

Error

指的是 Java 執行時內部錯誤和資源耗盡錯誤。 應用程式不丟擲此類異常。 這種內部錯誤一旦出現,除了告知使用者並使程式終止之外, 再無能無力, 如遞迴時造成的棧溢位,堆溢位。

Exception

是我們程式猿所使用的異常類的父類,它的直接子類有很多,圖中只展示了3種。

Exception

有一個子類稱為

RuntimeException

, 這裡面又派生出很多我們常見的異常類

NullPointerException

IndexOutOfBoundsException

等。

Error

類與

RuntimeException

類,為非受查異常,其他異常類為受查異常。

受查異常必須使用

try。。。catch

語句進行處理,否則報錯,非受查異常可以使用

try。。。catch

語句,也可以不使用。

異常的種類有很多, 我們要根據不同的業務場景來決定。

對於比較嚴重的問題(例如和算錢相關的場景), 應該讓程式直接崩潰, 防止造成更嚴重的後果。

對於不太嚴重的問題(大多數場景), 可以記錄錯誤日誌, 並透過監控報警程式及時通知程式猿。

對於可能會恢復的問題(和網路相關的場景), 可以嘗試進行重試。

在我們當前的程式碼中採取的是經過簡化的第二種方式。 我們記錄的錯誤日誌是出現異常的方法呼叫資訊, 能很快速的讓我們找到出現異常的位置。 以後在實際工作中我們會採取更完備的方式來記錄異常資訊。

2。4異常處理的注意事項

2。4。1異常捕捉時的注意事項

程式發生異常未被

try

捕獲處理,將會交給JVM處理,一旦交給JVM處理,程式直接非正常終止。

catch

語句捕獲異常的順序,從子類到父類,但是最好使用具體異常類處理。

一旦

try

中出現異常, 那麼

try

程式碼塊中的程式就不會繼續執行, 而是交給

catch

中的程式碼來執行,

catch

執行完畢會繼續往下執行程式後續程式碼。

finally

裡面的程式碼,無論異常是否被捕獲,都會執行。

由於

Exception

類是所有異常類的父類, 因此可以用這個型別表示捕捉所有型別的異常。

try

語句需要搭配

catch

finally

使用。

2。4。2異常處理的流程

程式先執行 try 中的程式碼。

如果 try 中的程式碼出現異常, 就會結束 try 中的程式碼, 看和 catch 中的異常型別是否匹配。

如果找到匹配的異常型別, 就會執行 catch 中的程式碼。

如果沒有找到匹配的異常型別, 就會將異常向上傳遞到上層呼叫者。

無論是否找到匹配的異常型別, finally 中的程式碼都會被執行到(在該方法結束之前執行)。

如果上層呼叫者也沒有處理的了異常, 就繼續向上傳遞,一直到 main 方法也沒有合適的程式碼處理異常, 就會交給 JVM 來進行處理, 此時程式就會異常終止。

2。5手動丟擲異常

除了 Java 內建的類會丟擲一些異常之外, 程式猿也可以手動丟擲某個異常,使用

throw

關鍵字完成這個操作。

public class Test13 { public static int divide(int a, int b) { if (b == 0) { throw new ArithmeticException(“除0異常!”); } return a / b; } public static void main(String[] args) { System。out。println(divide(12, 0)); }}

JavaSE系列】Java異常處理機制

我們在處理異常的時候, 通常希望知道這段程式碼中究竟會出現哪些可能的異常,我們可以使用

throws

關鍵字, 把可能丟擲的異常顯式的標註在方法定義的位置, 從而提醒呼叫者要注意捕獲這些異常,相當於宣告效果。

public class Test14 { public static int divide(int a, int b) throws ArithmeticException{ if (b == 0) { throw new ArithmeticException(“除0異常!”); } return a / b; } public static void main(String[] args) { try { System。out。println(divide(12, 0)); } catch (ArithmeticException e) { e。printStackTrace(); } }}

JavaSE系列】Java異常處理機制

3。自定義異常

Java內建了很多的異常,但是總會有需求在內建的異常中找不到合適的,這個時候就需要自己定義一個合適的異常,來應對需求。

我們以一個使用者登入來介紹自定義異常的使用,在此之前,需要知道:

自定義的異常需要繼承Java內建的異常類,通常會繼承自

Exception

或者

RuntimeException

,繼承

Exception

表示自定義的異常時受查異常,繼承

RuntimeException

表示自定義的異常是非受查異常。

使用者進行輸入賬號密碼登入時,可能會出現賬號不存在或者密碼錯誤的情況,這兩種情況是異常情況,需要進行處理,所以可以自定義兩個類

NameException

PasswordException

,前者表示賬號不存在異常,後者表示密碼錯誤異常,由於該異常是在使用者輸入時才會出現,所以不妨繼承

RuntimeException

,定義為非受查異常。

class NameException extends RuntimeException { public NameException(String exceptionMessage) { super(exceptionMessage); }}class PasswordException extends RuntimeException { public PasswordException(String exceptionMessage) { super(exceptionMessage); }}

模擬使用者登入:

public class Test15 { private static String userName = “未見花聞”; private static String password = “5201314”; public static void login(String userName, String password) throws NameException, PasswordException{ if (!Test15。userName。equals(userName)) { throw new NameException(“使用者名稱不存在!”); } if (!Test15。password。equals(password)) { throw new PasswordException(“密碼錯誤!”); } System。out。println(“登陸成功”); } public static void main(String[] args) { try { login(“csdn”, “123456”); }catch (NameException | PasswordException e) { e。printStackTrace(); } try { login(“未見花聞”, “123456”); }catch (NameException | PasswordException e) { e。printStackTrace(); } System。out。println(“============”); try { login(“未見花聞”, “5201314”); }catch (NameException | PasswordException e) { e。printStackTrace(); } }}

JavaSE系列】Java異常處理機制

本篇文章就到這了,接下來將介紹Java集合框架及其背後的資料結構。覺得文章寫得不錯的老鐵們,點贊評論關注走一波!謝謝啦!

Top