🌹🌹🌹個人主頁🌹🌹🌹
【🌹🌹🌹Java SE 專欄🌹🌹🌹】
🌹🌹🌹上一篇文章:【Java SE】帶你在String類世界中遨游!!!🌹🌹🌹
文章目錄
- 1. 異常的概念與體系結構
- 1.1 異常的概念
- 1.2 異常的體系結構
- 1.3 異常的分類
- 2. 異常的處理
- 2.1 防御式編程
- 2.2 異常的拋出(throw)
- 2.3 異常的捕獲(throws)
- 2.4 異常的處理流程
- 3. 自定義異常類
1. 異常的概念與體系結構
1.1 異常的概念
在Java中,將程序執行過程中發生的不正常行為稱為異常。如之前寫代碼時經常遇到的:
1. 算術異常
public static void main1(String[] args) {System.out.println(10/0);//算術異常//Exception in thread "main" java.lang.ArithmeticException: / by zeroat Exception.main(Exception.java:10)}
2. 數組越界異常
public static void main2(String[] args) {int[] array = {1,2,3};for(int i=0;i<=3;i++) {System.out.println(array[i]);//數組越界//Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: 3}}
3. 空指針異常
public static void main(String[] args) {int[] array = null;System.out.println(array.length);//空指針異常//Exception in thread "main" java.lang.NullPointerException}
從上述過程中可以看到,java中不同類型的異常,都有與其對應的類來進行描述。
1.2 異常的體系結構
異常種類繁多,為了對不同異常或者錯誤進行很好的分類管理,Java內部維護了一個異常的體系結構:
從上圖中可以看到:
- Throwable:是異常體系的頂層類,其派生出兩個重要的子類, Error 和 Exception
- Error:指的是Java虛擬機無法解決的嚴重問題,比如:JVM的內部錯誤、資源耗盡等,典型代表:StackOverflowError和OutOfMemoryError,一旦發生回力乏術。
- Exception:異常產生后程序員可以通過代碼進行處理,使程序繼續執行。比如:感冒、發燒。我們平時所說的異常就是Exception。
1.3 異常的分類
異常可能在編譯時發生,也可能在程序運行時發生,根據發生的時機不同,可以將異常分為:
1. 編譯時異常
在程序編譯期間發生的異常,稱為編譯時異常,也稱為受檢查異常(Checked Exception)
public class Person {
private String name;
private String gender;
int age;
// 想要讓該類支持深拷貝,覆寫Object類的clone方法即可
@Override
public Person clone() {
return (Person)super.clone();
}
}
編譯時報錯:
Error:(17, 35) java: 未報告的異常錯誤java.lang.CloneNotSupportedException; 必須對其進行捕獲或聲明以便拋出
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;
**缺陷:**正常流程和錯誤處理流程代碼混在一起, 代碼整體顯的比較混亂。
2. EAFP: It’s Easier to Ask Forgiveness than Permission. “事后獲取原諒比事前獲取許可更容易”. 也就是先操作, 遇到問題再處理. 即:事后認錯型
try {
登陸游戲();
開始匹配();
游戲確認();
選擇英雄();
載入游戲畫面();
...
} catch (登陸游戲異常) {
處理登陸游戲異常;
} catch (開始匹配異常) {
處理開始匹配異常;
} catch (游戲確認異常) {
處理游戲確認異常;
} catch (選擇英雄異常) {
處理選擇英雄異常;
} catch (載入游戲畫面異常) {
處理載入游戲畫面異常;
}
.....
優勢:正常流程和錯誤流程是分離開的, 程序員更關注正常流程,代碼更清晰,容易理解代碼
異常處理的核心思想就是 EAFP。
在Java中,異常處理主要的5個關鍵字:throw、try、catch、final、throws。
下面我們就來看看這五個關鍵字:
2.2 異常的拋出(throw)
在編寫程序時,如果程序中出現錯誤,此時就需要將錯誤的信息告知給調用者,比如:參數檢測。
在Java中,可以借助throw關鍵字,拋出一個指定的異常對象,將錯誤信息告知給調用者。具體語法如下:
throw new XXXException("異常產生的原因");
【需求】:實現一個獲取數組中任意位置元素的方法。
public static int getElement(int[] array, int index){
if(null == array){
throw new NullPointerException("傳遞的數組為null");
}
if(index < 0 || index >= array.length){
throw new ArrayIndexOutOfBoundsException("傳遞的數組下標越界");
}
return array[index];
}
public static void main(String[] args) {
int[] array = {1,2,3};
getElement(array, 3);
}
【注意事項】
- throw必須寫在方法體內部
- 拋出的對象必須是Exception 或者 Exception 的子類對象
- 如果拋出的是 RunTimeException 或者 RunTimeException 的子類,則可以不用處理,直接交給JVM來處理
- 如果拋出的是編譯時異常,用戶必須處理,否則無法通過編譯
- 異常一旦拋出,其后的代碼就不會執行
2.3 異常的捕獲(throws)
異常的捕獲,也就是異常的具體處理方式,主要有兩種:異常聲明throws 以及 try-catch捕獲處理.
2.3.1 異常聲明throws
處在方法聲明時參數列表之后,當方法中拋出編譯時異常,用戶不想處理該異常,此時就可以借助throws將異常拋給方法的調用者來處理。即當前方法不處理異常,提醒方法的調用者處理異常。
語法格式:
修飾符 返回值類型 方法名(參數列表) throws 異常類型1,異常類型2...{
}
【注意事項】
- throws必須跟在方法的參數列表之后
- 聲明的異常必須是 Exception 或者 Exception 的子類
- 方法內部如果拋出了多個異常,throws之后必須跟多個異常類型,之間用逗號隔開,如果拋出多個異常類型具有父子關系,直接聲明父類即可。
- 調用聲明拋出異常的方法時,調用者必須對該異常進行處理,或者繼續使用throws拋出
2.3.2 try-catch捕獲并處理
throws對異常并沒有真正處理,而是將異常報告給拋出異常方法的調用者,由調用者處理。如果真正要對異常進行處理,就需要try-catch。
語法格式:
try{
// 將可能出現異常的代碼放在這里
}catch(要捕獲的異常類型 e){
// 如果try中的代碼拋出異常了,此處catch捕獲時異常類型與try中拋出的異常類型一致時,或者是try中拋出異常的基類
時,就會被捕獲到
// 對異常就可以正常處理,處理完成后,跳出try-catch結構,繼續執行后序代碼
}[catch(異常類型 e){
// 對異常進行處理
}finally{
// 此處代碼一定會被執行到
}]
// 后序代碼
// 當異常被捕獲到時,異常就被處理了,這里的后序代碼一定會執行
// 如果捕獲了,由于捕獲時類型不對,那就沒有捕獲到,這里的代碼就不會被執行
注意:
1. []中表示可選項,可以添加,也可以不用添加
2. try中的代碼可能會拋出異常,也可能不會
【注意事項】’
- try塊內拋出異常位置之后的代碼將不會被執行
- 如果拋出異常類型與catch時異常類型不匹配,即異常不會被成功捕獲,也就不會被處理,繼續往外拋,直到JVM收到后中斷程序----異常是按照類型來捕獲的
- try中可能會拋出多個不同的異常對象,則必須用多個catch來捕獲----即多種異常,多次捕獲
- 可以通過一個catch捕獲所有的異常,即多個異常,一次捕獲(不推薦)
2.3.3 finally
在寫程序時,有些特定的代碼,不論程序是否發生異常,都需要執行,比如程序中打開的資源:網絡連接、數據庫連接、IO流等,在程序正常或者異常退出時,必須要對資源進進行回收。另外,因為異常會引發程序的跳轉,可能導致有些語句執行不到,finally就是用來解決這個問題的。
語法格式:
try{
// 可能會發生異常的代碼
}catch(異常類型 e){
// 對捕獲到的異常進行處理
}finally{
// 此處的語句無論是否發生異常,都會被執行到
}
// 如果沒有拋出異常,或者異常被捕獲處理了,這里的代碼也會執行
注意:finally中的代碼一定會執行的,一般在finally中進行一些資源清理的掃尾工作。
2.4 異常的處理流程
【異常處理流程總結】
程序先執行 try 中的代碼
如果 try 中的代碼出現異常, 就會結束 try 中的代碼, 看和 catch 中的異常類型是否匹配.
如果找到匹配的異常類型, 就會執行 catch 中的代碼
如果沒有找到匹配的異常類型, 就會將異常向上傳遞到上層調用者.
無論是否找到匹配的異常類型, finally 中的代碼都會被執行到(在該方法結束之前執行).
如果上層調用者也沒有處理的了異常, 就繼續向上傳遞.
一直到 main 方法也沒有合適的代碼處理異常, 就會交給 JVM 來進行處理, 此時程序就會異常終止.
3. 自定義異常類
Java 中雖然已經內置了豐富的異常類, 但是并不能完全表示實際開發中所遇到的一些異常,此時就需要維護符合我們實際情況的異常結構.
例如, 我們實現一個用戶登陸功能:
public class Test {public String name = "admin";public String passWorld = "123456";public void func(String name, String passWorld) {if(!this.name .equals(name)) {throw new userNameException("用戶名錯誤");}if(!this.passWorld.equals(passWorld)) {throw new passWorldException("密碼錯誤");}}public static void main(String[] args) {Test test = new Test();try {test.func("admin1","123456");} catch (userNameException e) {e.printStackTrace();System.out.println("用戶名輸入異常!");} catch (passWorldException e) {e.printStackTrace();System.out.println("密碼輸入異常!");}finally {System.out.println("結束進程");}}
}
具體方式:
- 自定義異常類,然后繼承自Exception 或者 RunTimeException
- 實現一個帶有String類型參數的構造方法,參數含義:出現異常的原因
自定義一個userNameException異常類:
public class userNameException extends RuntimeException{public userNameException() {super();}public userNameException(String name) {super(name);}
}
自定義一個passWorldException異常類:
public class passWorldException extends RuntimeException{public passWorldException() {super();}public passWorldException(String passWorld) {super(passWorld);}
}
注意事項
自定義異常通常會繼承自 Exception 或者 RuntimeException
繼承自 Exception 的異常默認是受查異常
繼承自 RuntimeException 的異常默認是非受查異常.
結尾:
希望大家可以給我點點關注,點點贊,你們的支持就是我的最大鼓勵。🌹🌹🌹🌹🌹🌹🌹🌹🌹🌹🌹🌹🌹🌹