『JavaSE』異常

本篇博客介紹Java中的異常機制及其基本使用。

什麼是異常?


首先,我們再來回顧一下剛開始接觸Java時犯的一些錯誤

  • 除0操作
    在這裏插入圖片描述
    在這裏插入圖片描述
  • 數組下標越界
    在這裏插入圖片描述
    在這裏插入圖片描述
  • 訪問null對象
    在這裏插入圖片描述
    在這裏插入圖片描述

上述幾個出錯程序運行之後打出的Exception開頭的紅色的提示就是異常信息。那什麼是異常呢?

  • 異常就是指程序運行時出現錯誤時通知程序調用者的一種機制
  • 注意異常時運行時的一種機制,不是編譯期。我們可以舉個例子來理解一下什麼是編譯期的錯誤,比如我們寫代碼時出現關鍵字的拼寫錯誤,此時出現的錯誤就是編譯期的錯誤。

防禦式編程


什麼是防禦式編程

  • 防禦式編程時提高軟件質量技術的有益輔助手段;
  • 防禦式編程的主要思想是:子程序應該不因傳入錯誤數據而被破壞,哪怕是由其他子程序產生的錯誤數據
  • 這種思想將可能出現的錯誤造成的影響控制在有限的範圍內。

錯誤在代碼中是客觀存在的。因此我們要讓程序出現問題的時候及時通知程序猿。主要有兩種方式:

  • LBYL:Look Before You Leap,在操作之前就做充分的檢查
  • EAFP:It’s Easier to Ask Forgiveness than Permission,先操作,遇到問題再處理

異常的核心思想就是EAFP(先操作,遇到問題再處理)

異常的優點


我們通過一個簡單的例子來看一下網絡通信LBYL和EAFP的處理流程

  • LBLY方式
    在這裏插入圖片描述
  • EAFP方式
    在這裏插入圖片描述

對比兩種風格的代碼,我們可以發現:

  • LBLY處理方式會將正常流程和錯誤處理流程代碼混在一起,代碼整體顯得比較混亂;
  • EAFP處理方式正常流程和錯誤處理流程是分開的,更容易理解代碼。

異常的基本用法


異常捕獲


基本語法如下

try {
	// 可能出現異常的語句;
} [catch (異常類型 異常對象) {
	// 異常的處理
} ...]
[finally {
	// 異常出口
}]
  • try代碼塊中放的是可能出現異常的代碼
  • catch代碼塊中放的是出現異常後的處理行爲
  • finally代碼塊中的代碼用於處理善後工作,會在最後執行
  • 其中catch和finally都可以根據情況選擇加或者不加

下面我們看幾種常見的異常處理方式

  • 不處理異常
    在這裏插入圖片描述
    在這裏插入圖片描述
    可以看到,如果對於異常不進行處理程序就會在出現異常處終止後序的代碼將不再執行。其實這裏異常並不是沒有被處理,而是被JVM處理JVM處理異常的方式就是打印出現異常的調用棧信息並終止程序
  • 使用try catch後的程序執行過程
    在這裏插入圖片描述
    在這裏插入圖片描述
    從運行結果可以看出,try中一旦有了異常,就會跳到對應的catch中,不再執行try中剩餘的邏輯
  • catch只能處理對應種類的異常
    在這裏插入圖片描述
    在這裏插入圖片描述
    可以看到,這裏的catch語句並沒有捕獲到數組訪問越界的異常,該異常最終被JVM處理
  • catch可以有多個
    在這裏插入圖片描述
    在這裏插入圖片描述
    一段代碼可能會拋出多種不同的異常,不同的異常有不同的處理方式。因此可以搭配多個catch代碼塊如果多個異常的處理方式是完全相同的,也可以寫成如下形式
    在這裏插入圖片描述
    在這裏插入圖片描述
  • 可以使用一個catch捕獲所有的異常Exception類是所有異常類的父類。因此可以用這個類型表示捕獲所有異常。catch進行匹配的時候,不僅可以捕捉到相同類型的異常,還可以捕捉到目標類型異常的子類對象。不推薦使用這種方式。
    在這裏插入圖片描述
    在這裏插入圖片描述
  • finally表示最後的善後工作,如釋放資源
    在這裏插入圖片描述
    在這裏插入圖片描述
    無論try中是否發生異常,finally中的代碼一定會執行
  • 可以使用try回收資源
    在這裏插入圖片描述
    和前一個代碼的寫法等價,將Scanner對象在try的()中創建,能夠保證在try執行完畢後自動調用Scanner的close方法
  • 如果當前方法中沒有合適的異常處理方式,異常就會沿着調用棧向上傳遞,直到最後交給JVM處理
    在這裏插入圖片描述
    在這裏插入圖片描述

異常處理流程


  • 程序先執行try中的代碼
  • 如果try中的代碼出現異常,就會結束try中的代碼,看和catch中的異常類型是否匹配
  • 如果找到匹配的異常類型,就會執行catch中的代碼
  • 如果沒有找到匹配的異常類型,就會將異常向上傳遞到上層調用者;
  • 無論是否找到匹配的異常類型,finally中的代碼都會被執行到在該方法結束之前執行);
  • 如果上層調用者也無法處理異常,異常就會繼續向上傳遞
  • 一直到main方法也沒有合適的代碼處理異常,就會交給JVM來進行處理,此時程序就會異常終止。

異常拋出


除了Java內置的類會拋出一些異常之外,程序猿也可以手動拋出某個異常。使用throw關鍵字來完成這個操作。
下面來看一個具體的例子
在這裏插入圖片描述
在這裏插入圖片描述

異常說明


我們在處理異常時,通常希望知道這段代碼中究竟會出現哪些可能的異常。我們可以使用throws關鍵字,把可能拋出的異常顯式的標註在方法定義的位置。從而提醒調用者要注意捕獲
在這裏插入圖片描述

finally的注意事項


我們知道finally中的代碼保證一定能夠被執行到,而且是在return語句之前,有時會帶來一些麻煩,我們看下面一個代碼:
在這裏插入圖片描述
在這裏插入圖片描述
注意

  • finally執行的時機是在方法返回之前try或者catch中如果有return會在這個return之前執行finally);
  • 但是如果finally中也存在return語句,那麼就會執行finally中的return,從而不會執行到try中原有的return
  • 不建議在finally中寫return語句,編譯器會有警告
    在這裏插入圖片描述

Java異常體系


在這裏插入圖片描述

  • 頂層類Throwable派生出兩個重要的子類,Error和Exception;
  • 其中Error指的是Java運行時內部錯誤和資源耗盡錯誤應用程序不拋出此類異常。這種內部錯誤一旦出現,除了告知用戶並使程序終止之外,沒有別的辦法,這種情況很少出現;
  • Exception是程序猿使用的異常類的父類
  • 其中Exception有一個子類稱爲RuntimeException,這裏面又派生出很多我們常見的異常類NullPointerException等

Java語言規範將派生於Error類或RuntimeException類的所有異常稱爲非受查異常,所有的其他異常稱爲受查異常
如果一段代碼可能拋出受查異常,那麼必須顯式進行處理
在這裏插入圖片描述
在這裏插入圖片描述
從報錯信息可以看出,我們必須對受查異常進行處理
這裏有兩種處理方式

  • 方法一使用try catch包裹起來進來
    在這裏插入圖片描述
    在這裏插入圖片描述
  • 方法二:在方法上加上異常說明,相當於將處理動作交給上級調用者
    在這裏插入圖片描述
    在這裏插入圖片描述

自定義異常類


Java中雖然已經內置了豐富的異常類,但是我們實際場景中可能還有一些情況需要我們對異常類進行擴展,創建符合我們實際情況的異常
這裏,我們模擬一個用戶登錄的場景:

  • 首先,我們自定義兩個異常類UserException和PasswordException
    在這裏插入圖片描述
  • 下面我們來寫程序主邏輯
    在這裏插入圖片描述
  • 運行效果如下
    在這裏插入圖片描述
    在這裏插入圖片描述
    在這裏插入圖片描述

注意

  • 自定義異常通常會繼承自Exception或者RuntimeException
  • 繼承自Exception的異常默認是受查異常
  • 繼承自RuntimeException的異常默認是非受查異常
  • 自定義異常類往往不是創建一個類,而是創建一個系列
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章