Java異常處理的五個關鍵字:try、catch、finally、throw、throws
1. 拋出異常throw
在編寫程序時,我們必須要考慮程序出現問題的情況。比如,在定義方法時,方法需要接受參數。那麼,當調用方法使用接受到的參數時,首先需要先對參數數據進行合法的判斷,數據若不合法,就應該告訴調用者,傳遞合法的數據進來。這時需要使用拋出異常的方式來告訴調用者。
在java中,提供了一個throw關鍵字,它用來拋出一個指定的異常對象。那麼,拋出一個異常具體如何操作呢?
-
創建一個異常對象。封裝一些提示信息(信息可以自己編寫)。
-
需要將這個異常對象告知給調用者。怎麼告知呢?怎麼將這個異常對象傳遞到調用者處呢?通過關鍵字throw就可以完成。throw 異常對象。
throw用在方法內,用來拋出一個異常對象,將這個異常對象傳遞到調用者處,並結束當前方法的執行。
使用格式:
throw new 異常類名(參數);
例如:
throw new NullPointerException("要訪問的arr數組不存在");
throw new ArrayIndexOutOfBoundsException("該索引在數組中不存在,已超出範圍");
學習完拋出異常的格式後,我們通過下面程序演示下throw的使用。
public class ThrowDemo {
public static void main(String[] args) {
//創建一個數組
int[] arr = {2,4,52,2};
//根據索引找對應的元素
int index = 4;
int element = getElement(arr, index);
System.out.println(element);
System.out.println("over");
}
/*
* 根據 索引找到數組中對應的元素
*/
public static int getElement(int[] arr,int index){
//判斷 索引是否越界
if(index<0 || index>arr.length-1){
/*
判斷條件如果滿足,當執行完throw拋出異常對象後,方法已經無法繼續運算。
這時就會結束當前方法的執行,並將異常告知給調用者。這時就需要通過異常來解決。
*/
throw new ArrayIndexOutOfBoundsException("哥們,角標越界了~~~");
}
int element = arr[index];
return element;
}
}
注意:如果產生了問題,我們就會throw將問題描述類即異常進行拋出,也就是將問題返回給該方法的調用者。
那麼對於調用者來說,該怎麼處理呢?一種是進行捕獲處理,另一種就是繼續將問題聲明出去,使用throws聲明處理。
2. Objects非空判斷
還記得我們學習過一個類Objects嗎,曾經提到過它由一些靜態的實用方法組成,這些方法是null-save(空指針安全的)或null-tolerant(容忍空指針的),那麼在它的源碼中,對對象爲null的值進行了拋出異常操作。
public static <T> T requireNonNull(T obj)
:查看指定引用對象不是null。
查看源碼發現這裏對爲null的進行了拋出異常操作:
public static <T> T requireNonNull(T obj) {
if (obj == null)
throw new NullPointerException();
return obj;
}
3. 聲明異常throws
聲明異常:將問題標識出來,報告給調用者。如果方法內通過throw拋出了編譯時異常,而沒有捕獲處理(稍後講解該方式),那麼必須通過throws進行聲明,讓調用者去處理。
關鍵字throws運用於方法聲明之上,用於表示當前方法不處理異常,而是提醒該方法的調用者來處理異常(拋出異常).
聲明異常格式:
修飾符 返回值類型 方法名(參數) throws 異常類名1,異常類名2…{ }
聲明異常的代碼演示:
public class ThrowsDemo {
public static void main(String[] args) throws FileNotFoundException {
read("a.txt");
}
// 如果定義功能時有問題發生需要報告給調用者。可以通過在方法上使用throws關鍵字進行聲明
public static void read(String path) throws FileNotFoundException {
if (!path.equals("a.txt")) {//如果不是 a.txt這個文件
// 我假設 如果不是 a.txt 認爲 該文件不存在 是一個錯誤 也就是異常 throw
throw new FileNotFoundException("文件不存在");
}
}
}
throws用於進行異常類的聲明,若該方法可能有多種異常情況產生,那麼在throws後面可以寫多個異常類,用逗號隔開。
public class ThrowsDemo2 {
public static void main(String[] args) throws IOException {
read("a.txt");
}
public static void read(String path)throws FileNotFoundException, IOException {
if (!path.equals("a.txt")) {//如果不是 a.txt這個文件
// 我假設 如果不是 a.txt 認爲 該文件不存在 是一個錯誤 也就是異常 throw
throw new FileNotFoundException("文件不存在");
}
if (!path.equals("b.txt")) {
throw new IOException();
}
}
}
4. 捕獲異常try…catch
如果異常出現的話,會立刻終止程序,所以我們得處理異常:
- 該方法不處理,而是聲明拋出,由該方法的調用者來處理(throws)。
- 在方法中使用try-catch的語句塊來處理異常。
try-catch的方式就是捕獲異常。
- 捕獲異常:Java中對異常有針對性的語句進行捕獲,可以對出現的異常進行指定方式的處理。
捕獲異常語法如下:
try{
編寫可能會出現異常的代碼
}catch(異常類型 e){
處理異常的代碼
//記錄日誌/打印異常信息/繼續拋出異常
}
**try:**該代碼塊中編寫可能產生異常的代碼。
**catch:**用來進行某種異常的捕獲,實現對捕獲到的異常進行處理。
注意:try和catch都不能單獨使用,必須連用。
演示如下:
public class TryCatchDemo {
public static void main(String[] args) {
try {// 當產生異常時,必須有處理方式。要麼捕獲,要麼聲明。
read("b.txt");
} catch (FileNotFoundException e) {// 括號中需要定義什麼呢?
//try中拋出的是什麼異常,在括號中就定義什麼異常類型
System.out.println(e);
}
System.out.println("over");
}
/*
*
* 我們 當前的這個方法中 有異常 有編譯期異常
*/
public static void read(String path) throws FileNotFoundException {
if (!path.equals("a.txt")) {//如果不是 a.txt這個文件
// 我假設 如果不是 a.txt 認爲 該文件不存在 是一個錯誤 也就是異常 throw
throw new FileNotFoundException("文件不存在");
}
}
}
如何獲取異常信息:
Throwable類中定義了一些查看方法:
public String getMessage()
:獲取異常的描述信息,原因(提示給用戶的時候,就提示錯誤原因。public String toString()
:獲取異常的類型和異常描述信息(不用)。public void printStackTrace()
:打印異常的跟蹤棧信息並輸出到控制檯。
包含了異常的類型,異常的原因,還包括異常出現的位置,在開發和調試階段,都得使用printStackTrace。
5. finally 代碼塊
finally:有一些特定的代碼無論異常是否發生,都需要執行。另外,因爲異常會引發程序跳轉,導致有些語句執行不到。而finally就是解決這個問題的,在finally代碼塊中存放的代碼都是一定會被執行的。
什麼時候的代碼必須最終執行?
當我們在try語句塊中打開了一些物理資源(磁盤文件/網絡連接/數據庫連接等),我們都得在使用完之後,最終關閉打開的資源。
finally的語法:
try…catch…finally:自身需要處理異常,最終還得關閉資源。
注意:finally不能單獨使用。
比如在我們之後學習的IO流中,當打開了一個關聯文件的資源,最後程序不管結果如何,都需要把這個資源關閉掉。
finally代碼參考如下:
public class TryCatchDemo4 {
public static void main(String[] args) {
try {
read("a.txt");
} catch (FileNotFoundException e) {
//抓取到的是編譯期異常 拋出去的是運行期
throw new RuntimeException(e);
} finally {
System.out.println("不管程序怎樣,這裏都將會被執行。");
}
System.out.println("over");
}
/*
*
* 我們 當前的這個方法中 有異常 有編譯期異常
*/
public static void read(String path) throws FileNotFoundException {
if (!path.equals("a.txt")) {//如果不是 a.txt這個文件
// 我假設 如果不是 a.txt 認爲 該文件不存在 是一個錯誤 也就是異常 throw
throw new FileNotFoundException("文件不存在");
}
}
}
當只有在try或者catch中調用退出JVM的相關方法,此時finally纔不會執行,否則finally永遠會執行。
6. 異常注意事項
-
多個異常使用捕獲又該如何處理呢?
- 多個異常分別處理。
- 多個異常一次捕獲,多次處理。
- 多個異常一次捕獲一次處理。
一般我們是使用一次捕獲多次處理方式,格式如下:
try{ 編寫可能會出現異常的代碼 }catch(異常類型A e){ 當try中出現A類型異常,就用該catch來捕獲. 處理異常的代碼 //記錄日誌/打印異常信息/繼續拋出異常 }catch(異常類型B e){ 當try中出現B類型異常,就用該catch來捕獲. 處理異常的代碼 //記錄日誌/打印異常信息/繼續拋出異常 }
注意:這種異常處理方式,要求多個catch中的異常不能相同,並且若catch中的多個異常之間有子父類異常的關係,那麼子類異常要求在上面的catch處理,父類異常在下面的catch處理。
-
運行時異常被拋出可以不處理。即不捕獲也不聲明拋出。
-
如果finally有return語句,永遠返回finally中的結果,避免該情況.
-
如果父類拋出了多個異常,子類重寫父類方法時,拋出和父類相同的異常或者是父類異常的子類或者不拋出異常。
-
父類方法沒有拋出異常,子類重寫父類該方法時也不可拋出異常。此時子類產生該異常,只能捕獲處理,不能聲明拋出