1. 程式人生 > >Java必知必會:異常機制詳解

Java必知必會:異常機制詳解

賦值 輸出結果 類庫 負數 虛擬 類名 通過反射 基於 all

一、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

:空指針異常。調用了不存在的對象或未經實例化或初始化的對象時會拋出,如當試圖操作一個空對象(賦值為null)的屬性、方法時就會拋出。

(實例化:通俗的理解就是為對象開辟空間,使其可在規定範圍內被調用。註意: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必知必會:異常機制詳解