Java必知必會:異常機制詳解
一、Java異常概述
在Java中,所有的事件都能由類描述,Java中的異常就是由java.lang包下的異常類描述的。
1、Throwable(可拋出):異常類的最終父類,它有兩個子類,Error與Exception。
Throwable中常用方法有:
getCause():返回拋出異常的原因。如果 cause 不存在或未知,則返回 null。
getMeage():返回異常的消息信息。
printStackTrace():對象的堆棧跟蹤輸出至錯誤輸出流,作為字段 System.err 的值。
百牛信息技術bainiu.ltd整理發布於博客園
2、Error(錯誤):表示程序無法處理的錯誤,一般與程序員的執行操作無關。理論上這些錯誤是不允許發生的,如果發生,也不應該試圖通過程序去處理,所以Error不是try-catch的處理對象,而JVM一般的處理方式是終止發生錯誤的線程。Error類常見子類有VirtualMachineError與AWTError。
3、VirtualMachineError(虛擬機錯誤):表示虛擬機出現錯誤。
在Java運行時內存中,除程序計數器外的虛擬機棧、堆、方法區在請求的內存無法被滿足時都會拋出OutOfMemoryError;
而如果線程請求的棧深度超出虛擬機允許的深度時,就會拋出StackOverFlowError。
4、AWTError(AWT組件出錯):這個錯誤並不是很常用。但是提一下AWT與Swing的區別,AWT是使用操作系統中的圖形函數的抽象窗口工具,用C\C++編寫,為了實現Java“一次編譯,處處運行”的理念,AWT使用各個操作系統圖形函數的交集,所以功能較差,但運行速度較快,適合嵌入式Java;
而Swing組件是基於AWT編寫的圖形界面系統,它用純Java編寫,所以必然實現了“一次編譯,處處運行”,但相較於AWT運行速度較慢,適合PC使用。
5、Exception(異常):出現原因取決於程序,所以程序也理應通過try-catch處理。
異常分為兩類:可查異常與不可查異常。
可查異常:編譯器要求必須處理,否則不能通過編譯,使用try-catch捕獲或者throws拋出。常見的可查異常有IOException(IO錯誤)及其子類EOFExcption(文件已結束異常)、FileNotFound(文件未找到異常)。
不可查異常(也叫運行時異常):編譯期不會檢查,所以在程序中可不處理,但如果發生,會在運行時拋出。所以這類異常要盡量避免!常見的不可查異常都是RuntimeException類及其子類。
1’ NullPointerException
(實例化:通俗的理解就是為對象開辟空間,使其可在規定範圍內被調用。註意:User u;這只是一個對象聲明,並沒有進行實例化或初始化。
初始化:就是把實例化後的對象中的基本數據類型字段賦默認值或設定值,為非基本類型賦值null,對於static字段只會初始化一次。)
2’ ArithmeticException:算術條件異常。最常見的就是0作除數時會拋出。
3’ ClassNotFoundException:類未找到異常。在通過反射Class.forName(“類名”)來獲取類時,如果未找到則會拋出異常。
4’ ArrayIndexOutOfBoundsException:數組索引越界異常。當試圖操作數組的索引值為負數或大於等於數組大小時會拋出。
5’ NegativeArraySizeException:數組長度為負值異常。一般在初始化數組大小為負值時拋出。
6’ ArrayStoreException:數組類型不匹配值異常。例如將一個Object數組中加入一個Integer對象與一個String對象時,類型不匹配就會拋出。
7’ IllegalArgumentException:非法參數異常。會在使用Java類庫方法時傳入參數值越界時拋出。
二、異常處理
Java中的異常處理原則:必須聲明拋出異常或捕獲可查異常,允許忽略Error與不可查異常。
public class TestException {
public static void main(String[] args)
throws IOException{ //拋出可查IO異常
//throw是針對對象拋出的異常,throws是針對方法拋出的異常
FileWriter fileWriter = new FileWriter("output.txt");
String str = null; //str對象未經初始化
try {
fileWriter.write(str); //嘗試調用未經初始化的str對象,會拋出空指針異常
} catch (NullPointerException e) {
System.out.println("Catch NullPointerException!");
}finally{
str = "finally!";
fileWriter.write(str);
fileWriter.close();
}
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
執行結果:
控制臺:
Catch NullPointerException!
output.txt:
finally!
1、throws意為拋出,只要出現異常,就會創建對應異常對象,然後記錄異常時運行狀態等異常信息,交付給運行時系統處理。拋出異常是一定執行在捕獲之前的,沒有拋出就不會有捕獲。程序可以顯式使用throws來聲明拋出可查異常以通過編譯。
2、try-catch-finally異常捕獲語句:
try中是可能發生異常的程序段;
catch中依次編寫對應的異常處理器方法,當拋出異常後,由運行時系統在棧中從當前位置開始依次回查方法,直到找到合適的異常處理方法,如果未找到,則執行finally或直接結束程序運行。
finally :無論是否捕獲或處理異常,finally塊裏的語句都會被執行。
註意(很重要,面試常問):當在try塊或catch塊中遇到return語句時,finally語句塊將在方法返回之前被執行。
在以下4種特殊情況下,finally塊不會被執行:
1)在finally語句塊中拋出了異常且未處理。
2)在前面的代碼中用了System.exit()退出程序。
3)程序所在的線程死亡。
4)CPU出現異常被關閉。
3、try-catch-finally的執行順序
1’當沒有異常捕獲時,會跳過catch,直接執行finally塊。
public class TestException {
public static void main(String[] args){
try {
System.out.println("Hello world!");
} catch (NullPointerException e) {
System.out.println("Catch NullPointerException!");
}catch (ArithmeticException e) {
System.out.println("Catch ArithmeticException!");
}catch(Exception e){
System.out.println("Catch other Exception!");
}
finally{
System.out.println("Finally!");
}
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
輸出結果:
Hello world!
Finally!
2’ 當拋出運行時異常且沒有定義相應的異常處理方法,就會由JVM拋出異常。
public class TestException {
public static void main(String[] args){
String str = null;
int a = 2, b = 0;
//調用空對象的方法,會拋出空指針異常
System.out.println(str.length());
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
輸出結果:
Exception in thread “main” java.lang.NullPointerException
at Java面試.TestException.main(TestException.java:11)
3、當try捕獲到異常,catch語句塊裏有處理此異常的情況:在try語句塊中是按照順序來執行的,當執行到某一條語句出現異常時,程序將跳到catch語句塊,並與catch語句塊逐一匹配,找到與之對應的處理程序,其他的catch語句塊將不會被執行,而try語句塊中,出現異常之後的語句也不會被執行,catch語句塊執行完後,最後執行finally語句塊後的語句。
public class TestException {
public static void main(String[] args){
String str = null;
int a = 2, b = 0;
try {
//調用空對象的方法,會拋出空指針異常
System.out.println(str.length()); //語句1
//除數為0,會拋出數學運算錯誤
System.out.println("a/b=" + a/b); //語句2
} catch (NullPointerException e) {
System.out.println("Catch NullPointerException!");
}catch (ArithmeticException e) {
System.out.println("Catch ArithmeticException!");
}catch(Exception e){
System.out.println("Catch other Exception!");
}
finally{
System.out.println("Finally!");
}
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
輸出結果:
Catch NullPointerException!
Finally!
三、自定義一個異常類
通過繼承Exception類實現。
class MyException extends Exception { // 創建自定義異常類
String message; // 定義String類型變量
public MyException(String ErrorMessagr) { // 父類方法
message = ErrorMessagr;
}
public String getMessage() { // 覆蓋getMessage()方法
return message;
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
四、異常鏈與異常棧軌跡
public class TestException {
public static void main(String[] args){
TestException test = new TestException();
String str = null;
test.printStringMessage(str);
}
public void printStringMessage(String str){
System.out.println(str.length());
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
Exception in thread “main” java.lang.NullPointerException
at Java面試.TestException.printStringMessage(TestException.java:16)
at Java面試.TestException.main(TestException.java:12)
常規異常:有Java所定義的異常,不需要異常聲明,在未被try-catch的情況下,會被默認上報到main()方法。
異常的冒泡上傳機制:當一個異常對象產生了以後,其會按照調用層次(一般是方法的調用層次)進行冒泡,直到被try-catch處理,或上報至main()方法。
//自定義的異常類
public class MyException extends Exception{
String message; // 定義String類型變量
public MyException(String ErrorMessagr) { // 父類方法
message = ErrorMessagr;
}
public String getMessage() { // 覆蓋getMessage()方法
return message;
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
//測試異常鏈、冒泡機制
public class TestException {
void firstThrow()
throws MyException { //拋出自定義異常
System.out.println("Oringinally creat a MyException and throw it out");
throw new MyException("MyException"); //真正的拋出異常處
}
void secondThrow()
throws MyException { //拋出自定義異常
firstThrow(); //調用firstThrow()
}
public TestException()
throws MyException { //構造方法,拋出自定義異常
secondThrow(); //調用secondThrow()
}
public static void main(String[] args) {
try{
//調用構造方法
TestException testException=new TestException();
}
catch(MyException e)
{
e.printStackTrace();
System.out.println("Catch a my exception!");
}
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
輸出結果:
Oringinally creat a MyException and throw it out
Java面試.MyException: MyException
at Java面試.TestException.firstThrow(TestException.java:11)
at Java面試.TestException.secondThrow(TestException.java:16)
at Java面試.TestException.(TestException.java:20)
at Java面試.TestException.main(TestException.java:26)
Catch a my exception!
從異常棧的記錄信息可以發現,與代碼相對應的異常拋出機制和次序:
firstThrow()產生MyException對象->異常冒泡至調用其的secondThrow()->冒泡至調用secondThrow()的TestExceptionChain的構造方法->冒泡至printtry的main()方法。
註意到:異常對象一直被拋出,直至在printtry的mian()方法中被try-catch捕獲!
Java必知必會:異常機制詳解