1、什么是異常
在生活當中,不管是人還是動物又或是植物,都會生病;在程序中也是,作為程序猿,雖然我們會盡力將程序寫的完美,可難免會出現一些問題~
在程序執行過程中,發生的一些不正常行為,就叫做異常。
2、異常的體系結構
我們可以觀察到,Throwable是異常體系的頂層類,其派生出兩個子類Exception(異常)和Error(錯誤)。
Exception:就是我們平時說的異常,可以理解為我們現實生活中的生病,我們可以通過代碼來處理異常,使程序正常運行。
Error:指的是Java虛擬機無法解決的嚴重問題,比如:JVM的內部錯誤、資源耗盡等,例如:StackOverflowError(棧溢出錯誤),一旦發生就回力乏術。
3、異常的分類
Exception繼承于Throwable,也就是說,異常其實是一個類,而異常又分為運行時異常(非受查異常)和編譯時異常(受查異常)。
3.1?運行時異常(非受查異常)
其實大家對非受查異常并不陌生,我們平時遇到的例如:數組越界異常、空指針異常、算數異常、類型轉換異常等都是非受查異常。
算數異常:
數組越界異常:
空指針異常:
3.2?編譯時異常(受查異常)
在程序編譯期間發生的異常,稱為編譯時異常,也稱為受檢查異常。
例如當我們拷貝自定義類型的對象時,我們沒有在main方法中聲明異常,就會劃紅線報錯:
受查異常必須進行處理,否則程序無法運行。
拋出異常后,異常警告消失,程序可以執行了。
但是需要注意的是,我們這里只是聲明了異常,并沒有處理掉這個異常,如果出現了異常,只會交給JVM來處理。一旦交給JVM處理,程序就會立即終止。
那,我們該如何去處理異常呢?
4、異常的處理
在Java中,異常處理主要用到5個關鍵字:throws、try、catch、throw、finally
4.1 throws?聲明異常
我們在上述舉例受查異常時就已經提到了throws關鍵字,當程序中可能會拋出編譯時異常時,我們可以使用throws來聲明這個異常,告訴調用者:"你要幫我處理掉這個異常!"
也就是說當前方法不出理這個異常,而提醒方法的調用者,讓調用者幫它處理。
當前方法不處理異常而使用throws聲明異常后,那該方法的調用者只有兩條路走:
1. 老老實實的幫它處理掉這個異常(使用try{}catch{} ,下面會講)
2. 不想幫它處理異常,也使用throws來聲明這個異常(就是我們前面舉例受查異常時所用到的解決方法)
也就是說,當編譯時異常出現后,必須進行處理!(方法內部處理,或者方法調用者來處理,總之必須處理!)否則程序無法運行!
對于throws關鍵字有以下幾點值得注意:
1. throws必須跟在方法的參數列表之后
2. 聲明的異常必須是 Exception 或者 Exception 的子類
3. 方法內部如果拋出了多個異常,throws之后必須跟多個異常類型,之間用逗號隔開,如果拋出多個異常類型 具有父子關系,直接聲明父類即可
4. 調用聲明拋出異常的方法時,調用者必須對該異常進行處理,或者繼續使用throws拋出
4.2 try-catch 捕獲處理異常
異常拋出后throws并沒有處理異常,只是進行了聲明,要想捕獲處理異常,需要用到try-catch。
語法格式:
我們在try{ }代碼塊中放入可能出現異常的代碼,使用catch來進行捕捉,
代碼舉例:
我們來運行上圖的代碼:
需要注意的是:
1. 當程序拋出異常后,如果catch中有該異常的捕捉,則程序會直接跳到這個catch塊執行catch中的代碼,并從這個catch塊繼續往下執行(程序不會異常終止)。如果catch中沒有捕捉到該異常,則會交給JVM來處理,程序也會立即終止。(也就是說我們可以用catch來捕捉多個異常,以免異常被交給JVM處理使程序異常終止)
2. try塊中只會拋出一個異常,當異常被拋出,會立即來到對應的catch中進行捕捉,try塊內拋出異常位置之后的代碼將不會被執行,也就是說即使try塊后面的代碼有異常,也不會再拋出,所以不會拋出多個異常。
對于printStackTrace方法的作用,就是打印出該異常出現的位置,便于程序猿的發現,
如下圖講解:
try-catch知識點總結:
1. try塊內拋出異常位置之后的代碼將不會被執行
2. 如果拋出異常類型與catch時異常類型不匹配,即異常不會被成功捕獲,也就不會被處理,繼續往外拋,直到 JVM收到后中斷程序----異常是按照類型來捕獲的
3.?try中可能會拋出多個不同的異常對象,則必須用多個catch來捕獲----即多種異常,多次捕獲
4.如果異常之間具有父子關系,一定是子類異常在前catch,父類異常在后catch,否則語法錯誤
5.由于 Exception 類是所有異常類的父類,因此可以用這個類型表示捕捉所有異常(可以放到最后用來兜底,但是極不推薦只使用Exception來捕獲異常 )
4.3 throw 手動拋出異常
我們之前講到的異常的拋出,都是由于觸發了JVM的機制由JVM來拋出的異常,這里的throw關鍵字是用來手動拋出異常的
例如:
因為異常都是一個類,所以我們throw出相應異常的對象就可以,也可以在構造方法傳入相關信息。
其實,throw主要用于拋出自定義類型的異常。
需要注意:
1. throw必須寫在方法體內部
2. 拋出的對象必須是Exception 或者 Exception 的子類對象(不能拋出自定義類的對象)
3. 如果拋出的是 RunTimeException 或者 RunTimeException 的子類(運行時異常),則可以不用處理,直接交給JVM來處理(但程序會立即終止)
4. 如果拋出的是編譯時異常,用戶必須處理,否則無法通過編譯
5. 異常一旦拋出,其后的代碼就不會執行
4.4 finally?
不管有沒有拋出異常,是否被捕獲,finally中的代碼一定會執行的,一般在finally中進行一些資源清理的掃尾工作。
比如,當程序拋出異常時,要么異常被catch捕獲,執行catch后代碼;要么異常沒有被catch捕獲,程序異常終止。這兩種情況都是會使程序的某些部分沒有被執行,而程序中會有必要的部分必須被執行,例如:資源的關閉,那就可以把這段必須被執行的代碼放入finally中。
其實,我們平時用的輸入方法就是一種資源,我們利用finally來關閉它:
運行1:
運行2:
運行3:
我們發現,不管有沒有拋出異常,也不管拋出異常后catch有沒有捕獲,哪怕是交給JVM來處理異常(程序異常終止),finally中的代碼都被執行了。
我們還可以提前將方法返回:
我們發現,即使方法已經遇見return返回,后面finally中的代碼仍然被執行了。
5、自定義異常類
Java當中雖說有著豐富的異常類,但是我們在開發過程中難免會遇見一些不能表示的異常,這時,我們就可以自定義異常。
如何自定義異常呢?
我們可以參考Java給出的異常源碼(僅當參考):
仿照源碼,創建自定義類,使之繼承于Exception 或者 RuntimeException類,給出無參和帶參的構造方法。
我們可以模擬實現用戶登錄界面,當用戶名或者密碼輸入錯誤時,可以拋出自定義的用戶名異常或者密碼異常:
首先,寫出自定義的用戶名異常和密碼異常:
當輸入用戶名或者密碼錯誤時會拋出對應異常:
運行展示:
1.密碼輸入錯誤
2.用戶名輸入錯誤
3.輸入正確
創建自定義異常類需要注意以下幾點:
1. 自定義異常通常會繼承自 Exception 或者 RuntimeException
2. 繼承自 Exception 的異常默認是受查異常(必須捕獲處理掉異常)
3. 繼承自 RuntimeException 的異常默認是非受查異常
OK~本次博客到這里就結束了,
感謝大家的閱讀~歡迎大家在評論區交流問題~
如果博客出現錯誤可以提在評論區~
創作不易,請大家多多支持~