Java異常


異常概述

異常也稱為例外,是在程序運行過程中發生的並且會打斷程序正常執行的事件,比如算術異常、空指針異常、文件找不到異常。所以在程序設計時,必須考慮到可能發生的異常事件,並做出相應的處理。這樣才能保證程序可以正常運行。

Java的異常處理機制也秉承着面向對象的基本思想,在Java中,所有的異常都是以類的形式存在,除了內置的異常類之外,Java也可以自定義異常類。此外,Java的異常處理機制也允許自定義拋出異常。

 

為什么要使用異常

在沒有異常處理的語言中,就必須使用if或switch等語句,配合所想得到的錯誤狀況來捕捉程序里所有可能發生的錯誤。但為了捕捉這些錯誤,編寫出來的代碼經常有很多的if語句,有時候這樣也未必能捕捉到所有的錯誤,而且這樣做勢必導致程序運行效率的降低。Java的異常處理機制恰好改進了這一點,它易於使用並且可自行定義異常類,但是不得不說,異常也是一個較為耗費性能的操作,因此我們需要合理地利用Java的異常處理機制,以增進程序的穩定性和效率。

 

異常的繼承架構

異常可分為兩大類:java.lang.Exception與java.lang.Error,這兩個類均繼承自java.lang.Throwable,上圖為Throwable類的繼承關系圖。

習慣上將Error與Exception統稱為異常類,但這兩者本質上還是有不同的:

1、Error專門用來處理嚴重影響程序運行的錯誤,可是通常程序設計者不會涉及程序代碼去捕捉這種錯誤,其原因在於即使捕捉到它,也無法給予適當的處理,比如Java虛擬機出錯就屬於一種Error

2、Exception包含了一般性的異常,這些異常通常在捕捉到之后便可以做妥善的處理,以確保程序繼續運行

從繼承架構圖中可以看出,Exception擴展出數個子類,其中IOException、RuntimeException是較常用的兩種。RuntimeException即使不編寫異常處理的程序代碼,依然可以編譯成功,而這種異常必須是在程序運行時才有可能發生,比如數組索引值超出了范圍。與RuntimeException不同的是,IOException一定要編寫異常處理的程序代碼才行,它通常用來處理輸入/輸出相關的操作,如對文件的訪問、網絡的連接等。

當異常發生時,發生異常的語句代碼會拋出一個異常類的實例化對象,之后此對象與catch語句中的類的類型進行匹配,然后在相應的catch中進行處理。

 

try、catch、finally、throw、throws

當異常發生時,通常可以用兩種方法來處理:

1、交由Java默認的異常處理機制做處理,但這種處理方式,Java通常只能輸出異常信息,接着便終止程序的運行,比如:

 1 public static void main(String[] args) throws InterruptedException
 2 {
 3     operateNull(null);
 4     System.out.println("After operateNull");
 5 }
 6 
 7 public static char operateNull(String str)
 8 {
 9     return str.charAt(0);
10 }

返回結果為:

Exception in thread "main" java.lang.NullPointerException
    at com.xrq.test33.TestMain.operateNull(TestMain.java:13)
    at com.xrq.test33.TestMain.main(TestMain.java:7)

和"接着便終止程序的運行"一致,因為第4行的語句並沒有打印。

2、自行編寫try-catch-finally塊來捕捉異常,這種寫法的最大好處是,可以靈活操控程序的流程且可做出最適當的處理,比如:

 1 public static void main(String[] args) throws InterruptedException
 2 {
 3     try
 4     {
 5         Thread.sleep(1000);
 6         operateNull(null);
 7     } 
 8     catch (NullPointerException e)
 9     {
10         System.out.println("NullPointerException's catch block!");
11         throw e;
12     }
13     finally
14     {
15         System.out.println("NullPointerException's finally block!");
16     }
17 }
18 
19 public static char operateNull(String str)
20 {
21     return str.charAt(0);
22 }

返回結果為:

1 NullPointerException's catch block!
2 NullPointerException's finally block!
3 Exception in thread "main" java.lang.NullPointerException
4     at com.xrq.test33.TestMain.operateNull(TestMain.java:25)
5     at com.xrq.test33.TestMain.main(TestMain.java:10)

關於這段代碼解釋每個關鍵字:

(1)try表示要檢查的程序語句

(2)catch表示異常發生時的處理語句,可省略

(3)finally表示無論catch內部寫了什么,即使是return,也都會運行到的語句,可省略。finally常用於對某段待檢查的代碼做掃尾工作,比如ReentrantLock的unlock()、IO的close()

(4)throw表示拋出一個類的實例,注意實例二字,實例意味着throw出去的是一個實例化的異常對象。所以代碼的11行也可以寫為"throw new NullPointerException()",至於為什么不這么寫,因為寫"throw new NullPointerException()"並沒有返回結果中第4行、第5行中的異常堆棧。注意,throw關鍵字可以寫在任何地方,並不強制必須寫在catch塊中,運行到throw所在的行,打印異常並立即退出當前方法

(5)throws用於方法聲明,表示如果方法內的程序代碼可能會發生異常,且方法內又沒有使用代碼的代碼塊來捕捉這些異常時,則必須在聲明方法時一並指明所有可能發生的異常,以便讓調用此方法的程序得以做好准備來捕捉異常。比如"方法名稱(參數...) throws 異常類1, 異常類2, 異常類3...",也可以不這么麻煩,Exception是所有異常的父類,因此也可以直接"方法名稱(參數...) throws Exception"

 

異常場景匯總

異常細節諸多,之前我也一直有搞不清楚的地方,因此這里對多種場景做一個總結、匯總:

1、接口方法可以throws異常,但必須throws一個具體的異常,不能直接throws出去Exception

public interface InterfaceException
{
    void ExceptionMethod() throws NullPointerException;
}

2、接口方法throws異常,其實現類實現該方法的時候不強制必須拋出該異常,也可以任意拋出異常

public class InterfaceExceptionImpl implements InterfaceException
{
    public void ExceptionMethod()
    {
        
    }
}
public class InterfaceExceptionImpl implements InterfaceException
{
    public void ExceptionMethod() throws ArrayIndexOutOfBoundsException
    {
        
    }
}

3、catch塊內如果捕獲到了異常並且throw出去了e,那么方法之后的代碼都不會再運行了;catch塊內如果捕獲到了異常,但是沒有throw出去e,也沒有任何導致程序終止的語句,那么try...catch...finally之后的語句仍然可以繼續運行

4、捕獲異常不可以先catch (Exception e){...}再catch (NullPointerException e)

原因是"Unreachable catch block for NullPointerException. It is already handled by the catch block for Exception",即Java檢測到NullPointer這個異常是不可達的

5、方法A聲明throws異常,則調用方法A的代碼必須try...catch...該異常

6、方法A聲明throws出Exception,調用方法A的代碼必須try...catch...該異常,如果:

(1)有匹配異常的catch塊,則優先走匹配異常的catch塊

(2)如果沒有匹配異常的catch塊,但是有catch (Exception e){...},則走catch (Exception e){...}

(3)如果沒有沒有匹配的catch塊,則調用方法A的地方throw異常,方法終止

7、如果方法中沒有try...catch異常而該方法發生了異常,且方法聲明中有throws,那么發生的該異常可以被拋到調用方法的代碼中

8、如果方法中對某個代碼塊做了try...catch並且想把catch到的異常拋給調用方法的地方,那么:

(1)不可以只有throw沒有throws,這將會導致捕獲到的異常無法被拋給調用方法的地方

(2)不可以只有throws沒有throw,這將會導致編譯出錯

只有在catch塊中throw捕獲到的異常並且在方法聲明的地方throws異常,才可以將一個異常正確地拋給調用方法的地方


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM