一、什么是例外?
例外就是不正常的意思,就像生活中,正在路上行使的汽車,遇到了故障,發動機突然拋錨停止了運轉,導致汽車不能正常的行駛,這樣的情況,在程式開發程序中就叫做”例外“,例外就是指在程式的執行程序中出現的非正常的情況,而這種非正常的情況最侄訓導致JVM(java虛擬機)非正常的停止, 主要表現為在集成開發環境中,代碼上會出現紅色的下劃線亦或者是紅色的波浪線,在控制臺(console)上程式會運行中斷并會拋出一個例外資訊,
像在Java等面向物件的編程語言中,例外本身其實就是一個類(例外類),而產生例外其實就是創建了一個例外物件,并且拋出了該例外物件,Java中對例外的處理方式是中斷處理,
有一個很關鍵的點,朋友們要注意了,特別是初學者最容易搞混了,我們要注意,導致出現例外的錯誤原因,并不包括語法錯誤, 我們通常寫的源代碼就是”.java“后綴的檔案,都需要經過java編譯器也就是JVM編譯過后產生的一個”.class“后綴的檔案,最終才能運行出來,而這里,如果是語法出現錯了,那么就會導致代碼的編譯不通過,就不會產生位元組碼檔案,那么程式就根本運行不了,前面說的例外就是拋出一個例外類的例外物件,而現在程式無法運行就不會產生并拋出例外類的物件,所以說,例外并不包括語法錯誤,

那么,今天呢,小北有幸能和大家分享,小北在學習例外處理時所避的坑,
小北在學習時,已經整理好了自己的學習筆記,Java中的例外處理相關的知識以及初學者需要注意,需要重點掌握的知識點,今天分享給大家,
希望各位,走過的路過的友友們,大佬們,給小北點兒鼓勵,制作不容易
點贊👍 👍 👍 收藏+關注,一鍵三連走起!
二、例外的體系及分類
例外機制就是用來幫助我們找到程式中出現的問題,Java中例外有一個根類,是java.lang.Throwable類,所有的例外都是由它繼承而來的,java.lang包下的Error類和java.lang.Exception類,分別是其兩個重要的子類,我們平時經常說的例外其實就是指的Exception,
java.lang.Error 就是指的很嚴重的錯誤,也就是無法處理的錯誤,而這種錯誤我們只能事先避免,否則一旦出現,我們就必須修改源代碼,
java.lang.Exception: 表示例外,可以處理的錯誤,例外可以通過代碼的方式來處理(無需修改源代碼),使得程式能夠繼續運行,而例外是必須要處理的, 例外類有兩種例外;(1)程式運行期間的例外,也就是說在代碼運行期間會檢查有無例外產生,但是編譯時,編譯器不會進行檢查,(2)程式編譯期間的例外,在程式編譯期間進行檢查,如果沒處理例外,那么就會導致編譯失敗,

三、例外的產生程序分析
看個例子:
public static void main(String[] args) {
int[] array={1,2,3,4 };
int a= printelement(array,4);
System.out.println(a);
}
public static int printelement(int[] array,int i){
int a= array[i];
return a;
}
(Ctrl+Shift+F10) 執行報錯:
我們來分析一下例外的產生程序:
第一步:main方法中int a= printelement(arr,4);呼叫了printelement(int[] arr,int i)方法,來訪問陣列中的下標index=4的元素,但是陣列下標是從0開始的沒有4索引,那么這時JVM(java虛擬機)就會檢測到程式出現例外,JVM處理步驟是這樣的:
1.JVM會根據當前出現的,例外的產生原因,創建一個例外物件,并且該物件包含了產生例外的內容、原因和位置;
例如new ArrayIndexOutOfBoundsException: 4
2.因為方法printelement(int[] arr,int i)中沒有寫處理例外的代碼邏輯(拋出例外 / try-catch),所以JVM就會把該例外物件,拋出給呼叫該方法的呼叫者(這里是main方法)處理這個例外,
第二步:方法的呼叫者(main方法)接收到這個例外,若有處理例外的代碼邏輯,就會處理,若沒有,則繼續向呼叫者的上級呼叫者拋出這個例外,直到 ” 有人“ 來處理這個例外,
第三步:main方法的呼叫者是JVM,那么JVM就會接收到這個例外物件并處理,處理邏輯是這樣的:
1.將例外物件的資訊(內容、原因、位置)以紅色的字體列印到控制臺(console)
2.JVM會中止當前正在執行的java程式,也就是程式的中斷處理,
四、例外的處理
1、關鍵字throw
throw關鍵字,用來在指定的方法中拋出一個指定的例外物件
(1)使用格式:
throw new ...Exception("產生例外的原因“);
(2)注意:
1.throw寫在方法的內部
2.throw new 物件必須是Exception類物件或者是其子類的物件
3.throw 批出的例外物件,必須處理
3.1、throw new的是RuntimeException類物件或者是其子類的物件
那么就可以不處理,因為默認會交給JVM來處理
(jvm列印出例外物件的資訊并且中斷程式)
3.2、throw new的是編譯例外,
那么就必須處理這個例外,throws或者try -catch
public static void main(String[] args) {
int[] array1={1,2,3,4 };
int a= printelement(array1,4);
System.out.println(a);
int[] array2=null;
int b= printelement(array2,2);
System.out.println(b);
}
public static int printelement(int[] array,int i){
//1.使用throw拋出例外
//1.1 空指標例外 NullPointerException
if(array ==null){
throw new NullPointerException("陣列為空");
}
//1.2 陣列下標越界例外ArrayIndexOutOfBoundsException
if(i<0 || i>= array.length){
throw new ArrayIndexOutOfBoundsException("找不到索引,下標越界了");
}
int a= array[i];
return a;
}


注意:上面兩種例外都屬于運行時例外,都默認交給了JVM來處理
2、Object類中提供的一個判空方法
public static <T> T requireNonNull(T obj);
//查看指定參考物件不是null,對傳遞過來的引數物件判斷是不是為空
原始碼:
public static <T> T requireNonNull(T obj) {
if (obj == null)
throw new NullPointerException();
return obj;
}
有兩種多載的方法如圖: message------->資訊

3、宣告例外的關鍵字throws
宣告例外就是指將問題標記出來并且報告給方法呼叫者, 如果方法內通過throw拋出了編譯時例外,但是并沒有捕獲處理該例外,那么必須要使用throws關鍵字進行宣告,讓呼叫者來處理該例外,關鍵字throws運用在方法宣告上,用來表示當前方法不處理例外,只是提醒該方法的呼叫者來處理例外(也就是指:拋出例外)
宣告的格式:
修飾符 回傳值型別 方法名(引數) throws 例外類名1,例外類名2..[
throw new 例外類名1("產生例外的原因");
throw new 例外類名2("產生例外的原因");
...
}
使用throws關鍵字注意;
注意;
1.throws 關鍵字必須寫在方法宣告上
2.throws 關鍵字后面宣告的例外必須是Exception或者是其的子類
3.在方法的內部若是拋出了多個例外類物件,
那么throws后面必須也宣告對應的多個例外類
如果拋出的多個例外物件之間有著子父類的關系,
那么就直接宣告父類例外就行,
4.如果呼叫了一個宣告了批出例外的方法,那么就必須要處理該方法宣告的例外
要么繼續使用throws宣告拋出例外交給方法的呼叫者來處理,
依次往上拋出,直到最終拋出給JVM來處理
要么就使用try...catch自己來處理這個例外,
使用throws關鍵字來拋出例外有個缺點,就是當使用throws拋出一個例外時,那么產生了該例外的代碼之后的后續代碼就不會在執行了,因為JVM已經中斷處理了,
4、try-catch捕獲例外
使用try-catch的結構方式就是用來捕獲例外,
- 捕獲例外:捕獲例外就是指Java中對例外有著針對性的陳述句來進行捕獲例外,并且可以對出現的例外進行指定方式的處理,
使用throws關鍵字宣告的方法,不處理例外,而是將出現的例外拋出給方法的呼叫者來處理;
使用try-catch陳述句可以處理例外,對出現的例外以指定的方法來處理
使用格式 1:處理一個例外
try{
//可能會出現例外的代碼塊
}catch(定義例外變數,用于接收try中的例外物件){
//產生例外物件后,對例外進行處理的邏輯,一般會記錄到一個日志中
}
使用格式 2:處理多個例外
try{
//可能會出現例外的代碼塊
}catch(定義例外變數1,用于接收try中的例外物件1){
//產生例外物件后,對例外進行處理的邏輯,一般會記錄到一個日志中
}catch(定義例外變數2,用于接收try中的例外物件2){
//產生例外物件后,對例外進行處理的邏輯,一般會記錄到一個日志中
}catch(定義例外變數3,用于接收try中的例外物件3){
//產生例外物件后,對例外進行處理的邏輯,一般會記錄到一個日志中
}
...
使用格式 3:處理多個例外,并且最終無論有無例外產生都會執行一個代碼塊
try{
//可能會出現例外的代碼塊
}catch(定義例外變數1,用于接收try中的例外物件1){
//產生例外物件后,對例外進行處理的邏輯,一般會記錄到一個日志中
}
...
catch(定義例外變數3,用于接收try中的例外物件3){
//產生例外物件后,對例外進行處理的邏輯,一般會記錄到一個日志中
}finally{
//無論是否出現例外,都會執行的代碼塊
}
注意:
finally不能獨立使用,必須要和try一起使用;
finally一般會用于對資源的釋放回收處理,程式無論是否會出現例外最終都要進行資源回收釋放處理,
使用try-catch注意;
- 如果try中產生了例外,那么就會執行catch中的例外處理邏輯,執行完catch中的處理邏輯,就會繼續執行try-catch之后的代碼,
- 如果try中沒有產生例外,那么就不會執行catch中例外的處理逐輯,執行完try中的代碼后,就會繼續執行try-catch之后的代碼,
5.Throwable類中處理例外的3個方法
String getMessage(): 回傳throwable類例外物件的簡單概述,
String tostring() : 回傳throwabLe類的例外物件詳細描述資訊(字串),
void printStackTrace() :
JVM列印例外物件的方法,列印的例外資訊是最全面的
6.使用例外的注意事項
(1)、當有多個例外使用捕獲時的處理方式:
- 1.多個例外分別處理
- 2.多個例外一次性捕獲,然后分別處理(一次捕獲,多次處理)
- 3.多個例外一次性捕獲,一次性處理(捕獲一次,處理一次)
舉例說明:
//1.多個例外分別處理
//處理例外1
try{
//定義一個陣列下標越界例外
int[] ints={1,2,3,4,5,6};
int anInt = ints[6];
System.out.println(anInt);
//ArrayIndexOutOfBoundsException: 6
}catch (ArrayIndexOutOfBoundsException e){
System.out.println(e);
}
//處理例外2
try{
//定義一個集合下標越界例外
ArrayList<Integer> integers = new ArrayList<>();
integers.add(1);
integers.add(2);
integers.add(3);
integers.add(4);
integers.add(5);
integers.add(6);
Integer integer = integers.get(6);
System.out.println(integer);
//IndexOutOfBoundsException: Index: 6, Size: 6
}catch (IndexOutOfBoundsException e){
System.out.println(e);
}
// 2.多個例外一次性捕獲,然后分別處理(一次捕獲,多次處理)
try{
//定義一個陣列下標越界例外
int[] ints={1,2,3,4,5,6};
int anInt = ints[6];
System.out.println(anInt);
//ArrayIndexOutOfBoundsException: 6
//定義一個集合下標越界例外
ArrayList<Integer> integers = new ArrayList<>();
integers.add(1);
integers.add(2);
integers.add(3);
integers.add(4);
integers.add(5);
integers.add(6);
Integer integer = integers.get(6);
System.out.println(integer);
//IndexOutOfBoundsException: Index: 6, Size: 6
}catch (ArrayIndexOutOfBoundsException e){
System.out.println(e);
}catch (IndexOutOfBoundsException e){
System.out.println(e);
}
/*
* 使用一次捕獲多次處理時要注意:
* 1.如果catch里面定義的例外變數之間有著子父類的關系,
* 那么子類的例外就必須要寫在父類之上,否則try中拋出的例外物件
* 系統會由上向下執行catch來捕獲這個例外并處理例外,默認會處理父類例外而不會處理子類例外
* 例如: ArrayIndexOutOfBoundsException extends IndexOutOfBoundsException
* */
//3.多個例外一次性捕獲,一次性處理(捕獲一次,處理一次)
try{
//定義一個陣列下標越界例外
int[] ints={1,2,3,4,5,6};
int anInt = ints[6];
System.out.println(anInt);
//ArrayIndexOutOfBoundsException: 6
//定義一個集合下標越界例外
ArrayList<Integer> integers = new ArrayList<>();
integers.add(1);
integers.add(2);
integers.add(3);
integers.add(4);
integers.add(5);
integers.add(6);
Integer integer = integers.get(6);
System.out.println(integer);
//IndexOutOfBoundsException: Index: 6, Size: 6
} catch (IndexOutOfBoundsException e){
System.out.println(e);
}
(2)、子父類例外的注意事項
- 如果父類拋出了多個例外,那么子類在重寫父類方法時就必須要拋出和父類相同的例外 或者父類例外的子類 或者不拋出例外
- 如果父類方法中沒有拋出例外,那么子類重寫父類方法時,就也不能批出例外,若子類此時產生了例外那么只能捕獲處理try-catch,而不能宣告批出例外,
public class fuClass {
//function1拋出多個例外
public void function1() throws NullPointerException,IndexOutOfBoundsException{}
//function2拋出多個例外
public void function2() throws NullPointerException,IndexOutOfBoundsException{}
//function3拋出多個例外
public void function3() throws NullPointerException,IndexOutOfBoundsException{}
//function4沒有拋出例外
public void function4(){}
}
class ziClass extends fuClass {
// 重寫方法,拋出與父類相同的例外
public void function1() throws NullPointerException,IndexOutOfBoundsException{}
//重寫方法,拋出父類例外的子類例外
public void function2() throws StringIndexOutOfBoundsException{}
//重寫方法,不批出例外
public void function3(){}
/*
如果父類方法中沒有拋出例外,那么子類重寫父類方法時,就也不能批出例外,
若子類此時產生了例外那么只能捕獲處理try-catch,而不能宣告批出例外,*/
public void function4(){
try{
//可能出現例外代碼
}catch (ArrayIndexOutOfBoundsException e){
//自己處理例外
}
}
}
五、自定義的例外類
自定義例外也就是自己定義一個例外來滿足當前代碼的需求
自定義例外類格式1:
//自定義編譯時期的例外類,若方法內部拋出了例外,
// 那么方法呼叫者就要處理這個例外 try-catch 或者 throws
public class XXXException extends Exception {
//添加無參構造方法
//添加帶例外資訊的構造方法
}
自定義例外類格式2:
//自定義運行時期的例外類,若方法內部拋出了例外,
//那么方法不需要處理例外,而是把例外拋JVM來處理(中斷處理)
public class XXXException extends RuntimeException {
//添加無參構造方法
//添加帶例外資訊的構造方法
}
制作不易,各位友友們,大佬們給點鼓勵!
點贊👍 👍 👍 收藏+關注 一鍵三連走起!
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/289734.html
標籤:java
上一篇:JAVA面向物件之封裝、繼承、多型、抽象(詳細理解)
下一篇:Python作用域、閉包與裝飾器
