Java常見異常及處理方式總結(jié)
一、概述
異常指不期而至的各種狀況,它在程序運行的過程中發(fā)生。作為開發(fā)者,我們都希望自己寫的代碼 永遠(yuǎn)都不會出現(xiàn) bug,然而現(xiàn)實告訴我們并沒有這樣的情景。如果用戶在程序的使用過程中因為一些原因造成他的數(shù)據(jù)丟失,這個用戶就可能不會再使用該程序了。所以,對于程序的錯誤以及外部環(huán)境能夠?qū)τ脩粼斐傻挠绊?,我們?yīng)當(dāng)及時報告并且以適當(dāng)?shù)姆绞絹硖幚磉@個錯誤。
之所以要處理異常,也是為了增強(qiáng)程序的魯棒性。
異常都是從 Throwable 類派生出來的,而 Throwable 類是直接從 Object 類繼承而來。你可以在 Java SE 官方 API 文檔中獲取更多關(guān)于它們的知識。
二、異常分類
異常通常有四類:
- Error:系統(tǒng)內(nèi)部錯誤,這類錯誤由系統(tǒng)進(jìn)行處理,程序本身無需捕獲處理。
- Exception:可以處理的異常。
- RuntimeException:可以捕獲,也可以不捕獲的異常。
- 繼承 Exception 的其他類:必須捕獲,通常在 API 文檔中會說明這些方法拋出哪些異常。
平時主要關(guān)注的異常是 Exception 下的異常,而 Exception 異常下又主要分為兩大類異常,一個是派生于 RuntimeExcption 的異常,一個是除了 RuntimeExcption 體系之外的其他異常。
RuntimeExcption 異常(運行時異常)通常有以下幾種:
- 錯誤的類型轉(zhuǎn)換
- 數(shù)組訪問越界
- 訪問
null指針 - 算術(shù)異常
一般來說,RuntimeException 都是代碼邏輯出現(xiàn)問題。
非 RuntimeException(受檢異常,Checked Exception)一般有:
- 打開一個不存在的文件
- 沒有找到具有指定名稱的類
- 操作文件異常
受檢異常是編譯器要求必須處理的異常,必須使用 try catch 處理,或者使用 throw 拋出,交給上層調(diào)用者處理。
三、聲明及拋出
throw 拋出異常
當(dāng)程序運行時數(shù)據(jù)出現(xiàn)錯誤或者我們不希望發(fā)生的情況出現(xiàn)的話,可以通過拋出異常來處理。
異常拋出語法:
throw new 異常類();
新建 ThrowTest.java:
public class ThrowTest {
public static void main(String[] args) {
Integer a = 1;
Integer b = null;
//當(dāng)a或者b為null時,拋出異常
if (a == null || b == null) {
throw new NullPointerException();
} else {
System.out.println(a + b);
}
}
}
運行:
Exception in thread "main" java.lang.NullPointerException
at ThrowTest.main(ThrowTest.java:8)
throws 聲明異常
throws 用于聲明異常,表示該方法可能會拋出的異常。如果聲明的異常中包括 checked 異常(受檢異常),那么調(diào)用者必須捕獲處理該異?;蛘呤褂?throws 繼續(xù)向上拋出。throws 位于方法體前,多個異常之間使用 , 分割。
新建ThrowsTest.java:
import java.io.FileInputStream;
import java.io.FileNotFoundException;
public class ThrowsTest {
public static void main(String[] args) throws FileNotFoundException {
//由方法的調(diào)用者捕獲異?;蛘呃^續(xù)向上拋出
throwsTest();
}
public static void throwsTest() throws FileNotFoundException {
new FileInputStream("/home/project/shiyanlou.file");
}
}
編譯運行:
Exception in thread "main" java.io.FileNotFoundException: /home/project/shiyanlou.file (系統(tǒng)找不到指定的路徑。)
at java.io.FileInputStream.open0(Native Method)
at java.io.FileInputStream.open(FileInputStream.java:195)
at java.io.FileInputStream.<init>(FileInputStream.java:138)
at java.io.FileInputStream.<init>(FileInputStream.java:93)
at ThrowsTest.throwsTest(ThrowsTest.java:13)
at ThrowsTest.main(ThrowsTest.java:8)
四、捕獲異常
通常拋出異常后,還需要將異常捕獲。使用 try 和 catch 語句塊來捕獲異常,有時候還會用到 finally。
對于上述三個關(guān)鍵詞所構(gòu)成的語句塊,try 語句塊是必不可少的,catch 和 finally 語句塊可以根據(jù)情況選擇其一或者全選。你可以把可能發(fā)生錯誤或出現(xiàn)問題的語句放到 try 語句塊中,將異常發(fā)生后要執(zhí)行的語句放到 catch 語句塊中,而 finally 語句塊里面放置的語句,不管異常是否發(fā)生,它們都會被執(zhí)行。
你可能想說,那我把所有有關(guān)的代碼都放到 try 語句塊中不就妥當(dāng)了嗎?可是你需要知道,捕獲異常對于系統(tǒng)而言,其開銷非常大,所以應(yīng)盡量減少該語句塊中放置的語句。
新建 CatchException.java:
public class CatchException {
public static void main(String[] args) {
try {
// 下面定義了一個try語句塊
System.out.println("I am try block.");
Class<?> tempClass = Class.forName("");
// 聲明一個空的Class對象用于引發(fā)“類未發(fā)現(xiàn)異?!?
System.out.println("Bye! Try block.");
} catch (ClassNotFoundException e) {
// 下面定義了一個catch語句塊
System.out.println("I am catch block.");
e.printStackTrace();
//printStackTrace()的意義在于在命令行打印異常信息在程序中出錯的位置及原因
System.out.println("Goodbye! Catch block.");
} finally {
// 下面定義了一個finally語句塊
System.out.println("I am finally block.");
}
}
}
編譯運行:
I am try block.
I am catch block.
java.lang.ClassNotFoundException:
at java.lang.Class.forName0(Native Method)
at java.lang.Class.forName(Unknown Source)
at CatchException.main(CatchException.java:8)
Goodbye! Catch block.
I am finally block.
五、捕獲多個異常
在一段代碼中,可能會由于各種原因拋出多種不同的異常,而對于不同的異常,我們希望用不同的方式來處理它們,而不是籠統(tǒng)的使用同一個方式處理,在這種情況下,可以使用異常匹配,當(dāng)匹配到對應(yīng)的異常后,后面的異常將不再進(jìn)行匹配。
新建源代碼文件 MultipleCapturesDemo.java:
import java.io.FileInputStream;
import java.io.FileNotFoundException;
public class MultipleCapturesDemo {
public static void main(String[] args) {
try {
new FileInputStream("");
} catch (FileNotFoundException e) {
System.out.println("IO 異常");
} catch (Exception e) {
System.out.println("發(fā)生異常");
}
}
}
編譯運行:
IO 異常
在處理異常時,并不要求拋出的異常同 catch 所聲明的異常完全匹配,子類的對象也可以匹配父類的處理程序。比如異常 A 繼承于異常 B,那么在處理多個異常時,一定要將異常 A 放在異常 B 之前捕獲,如果將異常 B 放在異常 A 之前,那么將永遠(yuǎn)匹配到異常 B,異常 A 將永遠(yuǎn)不可能執(zhí)行,并且編譯器將會報錯。
六、自定義異常
盡管 Java SE 的 API 已經(jīng)為我們提供了數(shù)十種異常類,然而在實際的開發(fā)過程中,你仍然可能遇到未知的異常情況。此時,你就需要對異常類進(jìn)行自定義。
自定義一個異常類非常簡單,只需要讓它繼承 Exception 或其子類就行。在自定義異常類的時候,建議同時提供無參構(gòu)造方法和帶字符串參數(shù)的構(gòu)造方法,后者可以為你在調(diào)試時提供更加詳細(xì)的信息。
百聞不如一見,下面我們嘗試自定義一個算術(shù)異常類。
創(chuàng)建一個 MyAriException 類:
主要的代碼如下:
// MyAriException.java
public class MyAriException extends ArithmeticException {
//自定義異常類,該類繼承自ArithmeticException
public MyAriException() {
}
//實現(xiàn)默認(rèn)的無參構(gòu)造方法
public MyAriException(String msg) {
super(msg);
}
//實現(xiàn)可以自定義輸出信息的構(gòu)造方法,將待輸出信息作為參數(shù)傳入即可
}
添加一個 ExceptionTest 類作為測試用,在該類的 main() 方法中,可以嘗試使用 throw 拋出自定義的異常。
代碼片段如下:
// ExceptionTest.java
import java.util.Arrays;
public class ExceptionTest {
public static void main(String[] args) {
int[] array = new int[5];
//聲明一個長度為5的數(shù)組
Arrays.fill(array, 5);
//將數(shù)組中的所有元素賦值為5
for (int i = 4; i > -1; i--) {
//使用for循環(huán)逆序遍歷整個數(shù)組,i每次遞減
if (i == 0) {
// 如果i除以了0,就使用帶異常信息的構(gòu)造方法拋出異常
throw new MyAriException("There is an exception occured.");
}
System.out.println("array[" + i + "] / " + i + " = " + array[i] / i);
// 如果i沒有除以0,就輸出此結(jié)果
}
}
}
檢查一下代碼,編譯并運行,期待中的自定義錯誤信息就展現(xiàn)在控制臺中了:
array[4] / 4 = 1
array[3] / 3 = 1
array[2] / 2 = 2
array[1] / 1 = 5
Exception in thread "main" MyAriException: There is an exception occured.
at ExceptionTest.main(ExceptionTest.java:17)
七、異常堆棧
當(dāng)異常拋出后,我們可以通過異常堆棧追蹤程序的運行軌跡,以便我們更好的 DEBUG。
新建一個 ExceptionStackTrace.java:
public class ExceptionStackTrace {
private static void method1() {
method2();
}
private static void method2() {
throw new NullPointerException();
}
public static void main(String[] args) {
try {
method1();
} catch (Exception e) {
//打印堆棧軌跡
e.printStackTrace();
}
}
}
編譯運行:
java.lang.NullPointerException
at ExceptionStackTrace.method2(ExceptionStackTrace.java:7)
at ExceptionStackTrace.method1(ExceptionStackTrace.java:3)
at ExceptionStackTrace.main(ExceptionStackTrace.java:11)
通過上面的異常堆棧軌跡,在對比我們方法的調(diào)用過程,可以得出異常信息中首先打印的是距離拋出異常最近的語句,接著是調(diào)用該方法的方法,一直到最開始被調(diào)用的方法。從下往上看,就可以得出程序運行的軌跡。
到此這篇關(guān)于Java常見異常及處理方式總結(jié)的文章就介紹到這了,更多相關(guān)Java異常內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Java中利用BitMap位圖實現(xiàn)海量級數(shù)據(jù)去重
有許多方法可以用來去重,比如使用列表、集合等等,但這些方法通常只適用于一般情況,然而,當(dāng)涉及到大量數(shù)據(jù)去重時,常見的 Java Set、List,甚至是 Java 8 的新特性 Stream 流等方式就顯得不太合適了,本文給大家介紹了Java中利用BitMap位圖實現(xiàn)海量級數(shù)據(jù)去重2024-04-04
淺談Java由于不當(dāng)?shù)膱?zhí)行順序?qū)е碌乃梨i
為了保證線程的安全,我們引入了加鎖機(jī)制,但是如果不加限制的使用加鎖,就有可能會導(dǎo)致順序死鎖(Lock-Ordering Deadlock)。本文將會討論一下順序死鎖的問題。2021-06-06
mybatis注解之@Mapper和@MapperScan的使用
這篇文章主要介紹了mybatis注解之@Mapper和@MapperScan的使用,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2021-10-10
java 數(shù)據(jù)結(jié)構(gòu)之堆排序(HeapSort)詳解及實例
這篇文章主要介紹了java 數(shù)據(jù)結(jié)構(gòu)之堆排序(HeapSort)詳解及實例的相關(guān)資料,需要的朋友可以參考下2017-03-03
淺談SpringBoot集成Redis實現(xiàn)緩存處理(Spring AOP實現(xiàn))
這篇文章主要介紹了淺談SpringBoot集成Redis實現(xiàn)緩存處理(Spring AOP實現(xiàn)),小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2017-12-12

