?目錄
1、異常的概念和體系結構
1.1、異常的概念
1.2、?異常的體系結構
1.3 異常的分類?
2、異常的處理
2.1、防御式編程
2.2、異常的拋出
2.3、異常的捕獲
2.3.1、異常聲明throws
2.3.2、try-catch捕獲并處理
3、自定義異常類
?
1、異常的概念和體系結構
1.1、異常的概念
在日常開發中,絞盡腦汁將代碼寫的盡善盡美,在程序運行過程中,難免會出現一些奇奇怪怪的問題。有時通過代碼很難去控制,比如:數據格式不對、網絡不通暢、內存報警等。
當出現這些問題時,JVM虛擬機會自動捕獲這些問題并拋出錯誤信息。在Java中,將程序執行過程中發生的不正常行為稱為異常。
例如:
(1)算術異常
System.out.println(10 / 0);
(2)數組越界異常
int[] arr = {1, 2, 3};
System.out.println(arr[100]);
(3)空指針異常
int[] arr = null;
System.out.println(arr.length);
從上述過程中可以看到,java中不同類型的異常,都有與其對應的類來進行描述。?
1.2、?異常的體系結構
異常種類繁多,為了對不同異常或者錯誤進行很好的分類管理,Java內部維護了一個異常的體系結構:
從上圖中可以看到:
- Throwable:是異常體系的頂層類,其派生出兩個重要的子類, Error 和 Exception
- Error:指的是Java虛擬機無法解決的嚴重問題,比如:JVM的內部錯誤、資源耗盡等,典型代表:StackOverflowError和OutOfMemoryError,一旦發生回力乏術。
- Exception:異常產生后程序員可以通過代碼進行處理,使程序繼續執行。比如:感冒、發燒。我們平時所說的異常就是Exception。
1.3 異常的分類?
異常可能在編譯時發生,也可能在程序運行時發生,根據發生的時機不同,可以將異常分為:
1. 編譯時異常
在程序編譯期間發生的異常,稱為編譯時異常,也稱為受檢查異常(Checked Exception)
2. 運行時異常
在程序執行期間發生的異常,稱為運行時異常,也稱為非受檢查異常(Unchecked Exception)
RunTimeException以及其子類對應的異常,都稱為運行時異常。比如:NullPointerException、ArrayIndexOutOfBoundsException、ArithmeticException。
注意:編譯時出現的語法性錯誤,不能稱之為異常。例如將 System.out.println 拼寫錯了, 寫成了system.out.println. 此時編譯過程中就會出錯, 這是 "編譯期" 出錯。而運行時指的是程序已經編譯通過得到class 文件了, 再由 JVM 執行過程中出現的錯誤。
2、異常的處理
2.1、防御式編程
錯誤在代碼中是客觀存在的. 因此我們要讓程序出現問題的時候及時通知程序猿。主要的方式
?1. LBYL: Look Before You Leap. 在操作之前就做充分的檢查。即:事前防御型
boolean ret = false;ret = 登陸游戲();if (!ret) {處理登陸游戲錯誤;return;}ret = 開始匹配();if (!ret) {處理匹配錯誤;return;}ret = 游戲確認();if (!ret) {處理游戲確認錯誤;return;}ret = 選擇英雄();if (!ret) {處理選擇英雄錯誤;return;}ret = 載入游戲畫面();if (!ret) {處理載入游戲錯誤;return;}
?缺陷:正常流程和錯誤處理流程代碼混在一起, 代碼整體顯的比較混亂。
2. EAFP: It's Easier to Ask Forgiveness than Permission. "事后獲取原諒比事前獲取許可更容易". 也就是先操作, 遇到問題再處理. 即:事后認錯型
try {登陸游戲();開始匹配();游戲確認();選擇英雄();載入游戲畫面();
...} catch (登陸游戲異常) {處理登陸游戲異常;} catch (開始匹配異常) {處理開始匹配異常;} catch (游戲確認異常) {處理游戲確認異常;} catch (選擇英雄異常) {處理選擇英雄異常;} catch (載入游戲畫面異常) {處理載入游戲畫面異常;}
優勢:正常流程和錯誤流程是分離開的, 程序員更關注正常流程,代碼更清晰,容易理解代碼?
2.2、異常的拋出
在編寫程序時,如果程序中出現錯誤,此時就需要將錯誤的信息告知給調用者,比如:參數檢測。
在Java中,可以借助throw關鍵字,拋出一個指定的異常對象,將錯誤信息告知給調用者。具體語法如下:
throw new XXXException("異常產生的原因");
【注意事項】
- throw必須寫在方法體內部
- 拋出的對象必須是Exception 或者 Exception 的子類對象
- 如果拋出的是 RunTimeException 或者 RunTimeException 的子類,則可以不用處理,直接交給JVM來處理
- 如果拋出的是編譯時異常,用戶必須處理,否則無法通過編譯
- 異常一旦拋出,其后的代碼就不會執行(除非使用try-catch捕捉可能異常代碼)
2.3、異常的捕獲
異常的捕獲,也就是異常的具體處理方式。
主要有兩種:異常聲明throws 以及 try-catch捕獲處理。
2.3.1、異常聲明throws
處在方法聲明時參數列表之后,當方法中拋出編譯時異常,用戶不想處理該異常,此時就可以借助throws將異常拋給方法的調用者來處理。即當前方法不處理異常,提醒方法的調用者處理異常。
語法格式:
修飾符 返回值類型 方法名(參數列表) throws 異常類型1,異常類型2...{}
【注意事項】
- throws必須跟在方法的參數列表之后。
- 聲明的異常必須是 Exception 或者 Exception 的子類。
- 方法內部如果拋出了多個異常,throws之后必須跟多個異常類型,之間用逗號隔開,如果拋出多個異常類型具有父子關系,直接聲明父類即可。
- 調用聲明拋出異常的方法時,調用者必須對該異常進行處理,或者繼續使用throws拋出。
2.3.2、try-catch捕獲并處理
throws對異常并沒有真正處理,而是將異常報告給拋出異常方法的調用者,由調用者處理。如果真正要對異常進行處理,就需要try-catch。
語法格式:
public static void main(String[] args) {try {// 將可能出現異常的代碼放在這里} catch (要捕獲的異常類型 e) {// 如果try中的代碼拋出異常了,此處catch捕獲時異常類型與try中拋出的異常類型一致時,或者是try中拋出異常的基類時,就會被捕獲到// 對異常就可以正常處理,處理完成后,跳出try-catch結構,繼續執行后序代碼} finally {// 此處代碼一定會被執行到}// 后序代碼// 當異常被捕獲到時,異常就被處理了,這里的后序代碼一定會執行// 如果捕獲了,由于捕獲時類型不對,那就沒有捕獲到,這里的代碼就不會被執行}
finally是什么?
finally中代碼是一定會被執行的。
在寫程序時,有些特定的代碼,不論程序是否發生異常,都需要執行,比如程序中打開的資源:網絡連接、數據庫連接、IO流等,在程序正常或者異常退出時,必須要對資源進進行回收。另外,因為異常會引發程序的跳轉,可能導致有些語句執行不到,finally就是用來解決這個問題的。?
注意:不是說放在try中的代碼就一定會拋出異常,只是在try內寫的代碼在拋出異常時會被捕捉到。需要理解一個誤區。?
關于異常的處理方式
異常的種類有很多, 我們要根據不同的業務場景來決定.
對于比較嚴重的問題(例如和算錢相關的場景), 應該讓程序直接崩潰, 防止造成更嚴重的后果
對于不太嚴重的問題(大多數場景), 可以記錄錯誤日志, 并通過監控報警程序及時通知程序猿
對于可能會恢復的問題(和網絡相關的場景), 可以嘗試進行重試。
我們記錄的錯誤日志是出現異常的方法調用信息, 能很快速的讓我們找到出現異常的位置. 以后在實際工作中我們會采取更完備的方式來記錄異常信息。
【異常處理的流程總結】
- 程序先執行 try 中的代碼。
- 如果 try 中的代碼出現異常, 就會結束 try 中的代碼, 看和 catch 中的異常類型是否匹配。
- 如果找到匹配的異常類型, 就會執行 catch 中的代碼。
- 如果沒有找到匹配的異常類型, 就會將異常向上傳遞到上層調用者。
- 無論是否找到匹配的異常類型, finally 中的代碼都會被執行到(在該方法結束之前執行)。
- 如果上層調用者也沒有處理的了異常, 就繼續向上傳遞。
- 一直到 main 方法也沒有合適的代碼處理異常, 就會交給 JVM 來進行處理, 此時程序就會異常終止。
3、自定義異常類
自定義異常類必須繼承自Exception或者RunTimeException,并且實現一個帶有String類型參數的構造方法,參數含義:出現異常的原因
例如, 我們實現一個用戶登陸功能。
//用于判斷用戶名的異常類
public class UserNameException extends RuntimeException{public UserNameException() {super();}public UserNameException(String s) {super(s);}
}
//用于判斷密碼的異常類
public class UserPasswordException extends RuntimeException {public UserPasswordException() {super();}public UserPasswordException(String s) {super(s);}
}
用戶進行登錄,用戶名或者密碼錯誤時需要提示用戶名或密碼錯誤,但是如果直接使用println打印,那么只能知道錯了,但是無法得知是錯在哪一行,而使用自定義異常類就能解決這個問題,方便程序員在大型工程中快速找到報錯地方。
class Login {public String userName = "admin";public String passWord = "123456";public void login(String userName, String passWord) {if(!this.userName.equals(userName)) {//System.out.println("用戶名錯誤!");//使用自定義異常類來提示用戶名錯誤throw new UserNameException("用戶名錯誤!");}if(!this.passWord.equals(passWord)) {//System.out.println("密碼錯誤!");//使用自定義異常類來提示密碼錯誤throw new UserPasswordException("密碼錯誤!");}}
}public class Main {public static void main(String[] args) {try {Login l = new Login();l.login("admin", "123");} catch (UserNameException | UserPasswordException e) {e.printStackTrace();}}
}
?【運行結果】
【注意事項】?
- 自定義異常通常會繼承自 Exception 或者 RuntimeException。
- 繼承自 Exception 的異常默認是受查異常。
- 繼承自 RuntimeException 的異常默認是非受查異常。
?
?博主推薦:
?【計算機組成原理】知識點鞏固 - 存儲器概述-CSDN博客https://blog.csdn.net/zzzzzhxxx/article/details/134482974?spm=1001.2014.3001.5502
【JavaSE】基礎筆記 - 圖書管理系統(保姆教程,含源碼)-CSDN博客https://blog.csdn.net/zzzzzhxxx/article/details/134467911?spm=1001.2014.3001.5502
【JavaSE】基礎筆記 - 類和對象(下)-CSDN博客https://blog.csdn.net/zzzzzhxxx/article/details/134248822?spm=1001.2014.3001.5502?
如果覺得作者寫的不錯,求給博主一個大大的點贊支持一下,你們的支持是我更新的最大動力!
如果覺得作者寫的不錯,求給博主一個大大的點贊支持一下,你們的支持是我更新的最大動力!
如果覺得作者寫的不錯,求給博主一個大大的點贊支持一下,你們的支持是我更新的最大動力!
?