Java 異常處理機制

Java 的異常處理機制可以讓程序具有極好的容錯性,讓程序更健壯。當程序運行出現意外情形時,系統會自動生成一個 Exception 對象來通知程序,實現將“業務功能實現代碼”和“錯誤處理代碼”分離,提供更好的可讀性。
本文包含:

  1. 使用 try…catch 捕獲異常
  2. 異常類的繼承體系
  3. Java 7 提供的多異常捕獲
  4. 訪問異常信息
  5. 使用 finally 回收資源
  6. 異常處理的嵌套
  7. Java 7 的自動關閉資源的 try 語句

1. 使用 try…catch 捕獲異常

Java 針對異常處理機制提出了一種假設:如果程序可以順利完成,那就“一切正常”,把系統的業務實現代碼放在 try 塊中定義,所有的異常處理邏輯放在 catch 塊中進行處理。
下面是 Java 異常處理機制的語法結構:
在這裏插入圖片描述
如果執行 try 塊裏的業務邏輯代碼時出現異常,系統自動生成一個異常對象,該異常對象被提交給 Java 運行時環境,這個過程被稱為拋出(throw)異常。
當 Java 運行時環境收到異常對象時,會尋找能出來該異常對象的 catch 塊,如果找到合適的 catch 塊,則把該異常對象交給 catch 塊出來,這個過程稱為捕獲(catch)異常;如果 Java 運行時環境找不到捕獲異常的 catch 塊,則運行時環境終止,Java 程序也將退出。
在這裏插入圖片描述
下面使用異常處理機制來實現代碼:
在這裏插入圖片描述
在這裏插入圖片描述
上面程序把處理用户輸入字符串的代碼都放在 try 塊裏運行,只要用户輸入的字符串不是有效的座標值(包括字母不能正確解析,沒有逗號不能正常解析,解析處理的座標引起數組越界…),系統都將拋出一個異常對象,並將異常對象交給 catch 塊(也就是上面程序中粗體字代碼塊)處理,catch 塊的處理方式是向用户提示座標不合理,然後使用 continue 忽略本次循環剩餘代碼,開始執行下一次循環,保證了該五子棋遊戲有足夠的容錯性—用户可以隨意輸入,程序不會因為用户輸入不合法而突然退出,程序會向用户提示輸入不合法,讓用户再次輸入。

2. 異常類的繼承體系

當 Java 運行時環境接收到異常對象時,如何為該異常對象尋找 catch 塊呢?注意上面的 Gobang 程序中 catch 關鍵字的形式:(Exception e),這意味着每個 catch 塊都是專門用於處理該異常類及子類的異常實例。
當 Java 運行環境接收到異常對象後,會依次判斷該異常對象是否是 catch 塊後異常類及其子類的實例,如果是,Java 運行時環境將調用該 catch 塊來處理異常;否則再次拿該異常對象和下一個 catch 塊裏的異常類進行比較,Java 異常捕獲流程示意圖如下:
在這裏插入圖片描述
當 程序進入負責異常處理的 catch 塊中時,系統生成的異常對象 ex 將會傳給 catch 塊後的異常參數,從而允許 catch 塊通過該對象來獲得異常的詳細信息。
try 塊後可以有多個 catch 塊,這是為了針對不同的異常類提供的不同的異常處理方式。當系統發生不同的意外情況時,系統會生成不同的異常對象,Java 允許時就會根據該異常對象所屬的異常類來決定使用哪個 catch 塊來處理該異常。
在這裏插入圖片描述
在通常情況下,如果 try 塊被執行一次,則 try 塊後只有一個 catch 塊會被執行,不可能有多個 catch 塊被執行。除非在循環中使用了 continue 開始下一次循環,下一次循環又重新運行了 try 塊,這才可能導致多個 try 塊被執行。
在這裏插入圖片描述
Java 提供了豐富的異常類,這些異常類有嚴格的繼承關係,如下圖所示:
在這裏插入圖片描述
Java 把所有的非正常情況分為兩種:異常(Exception)和 錯誤(Error),它們都繼承 Throwable 父類。
Error 錯誤,通常是與虛擬機相關的問題,如系統崩潰、虛擬機錯誤、動態鏈接失敗等,這些錯誤無法恢復或不可能捕獲,將導致應用程序中斷。通常應用程序無法處理這些錯誤,因此應用程序不應該試圖使用 catch 塊來捕獲 Error 對象。在定義該方法時,也無須在其 throws 子句中聲明該方法可能拋出 Error 及其任何子類。
在這裏插入圖片描述在這裏插入圖片描述
上面三種都是非常常見的異常。
在這裏插入圖片描述
在這裏插入圖片描述
程序總是把對應的 Exception 類的 catch 塊放在最後,因為如果把它反正前面,Java 運行時直接進入該 catch 塊(因為所有的異常對象都是 Exception 或其子類的實例),而排在它後面的 catch 塊將永遠也不會獲得執行的機會。
在這裏插入圖片描述
異常捕獲時,主要先捕獲小異常,再捕獲大異常。

3. Java 7 提供的多異常捕獲

在這裏插入圖片描述

4. 訪問異常信息

在這裏插入圖片描述
所有的異常對象都包含了如下幾個常用方法:
在這裏插入圖片描述
下面例子程序演示了程序如何訪問異常信息:
在這裏插入圖片描述
在這裏插入圖片描述
下面程序調用了 Exception 對象的 getMessage() 方法來得到異常對象的詳細信息,也使用了 printStackTrace() 方法來打印該異常的跟蹤信息。運行上面程序,會看到下圖界面:
在這裏插入圖片描述
上面程序中使用的 FileInputStream 是 Java IO 體系中的一個文件輸入流,用於讀取磁盤文件的內容。
從圖中可以看到異常的詳細描述信息:“a.txt (系統找不到指定的文件)”,這就是調用異常的 getMessage() 方法返回的字符串。

5. 使用 finally 回收資源

有時候,程序在 try 塊中打開了一些物理資源(例如數據庫連接、網絡連接和磁盤文件等),這些物理資源都必須顯式回收。
Java 的垃圾回收機制不會回收任何物理資源,只能回收堆內存中對象所佔用的內存。
Java 通過 finally 塊來回收 try 塊打開的物理資源。
在這裏插入圖片描述
在這裏插入圖片描述
除非在 try 塊、catch 塊中調用了退出虛擬機的方法,否則不管在 try 塊、catch 塊中執行怎樣的代碼,出現什麼樣的情況,異常處理的 finally 塊總會被執行。

在通常情況下,不要在 finally 塊中使用如 return 或 throw 等導致方法終止的語句,(throws 語句將在後面介紹),一旦在 finally 塊中使用了 return 或 throw 語句,將會導致 try 塊、catch 塊中的 return、throw語句失效。如下面程序:
在這裏插入圖片描述
在這裏插入圖片描述
儘量避免在 finally 塊裏使用 return 或 throws 等導致方法終止的語句。

6. 異常處理的嵌套

finally 塊中也包含了一個完整的異常處理流程,這種在 try 塊、catch 塊或 finally 塊中包含完整的異常處理流程的情形被稱為異常處理的嵌套。
在這裏插入圖片描述

7. Java 7 的自動關閉資源的 try 語句

在上面程序中,使用 finally 塊關閉資源時,程序較繁瑣
在這裏插入圖片描述在這裏插入圖片描述
在這裏插入圖片描述
在這裏插入圖片描述
下面程序示範瞭如何使用自動關閉資源的 try 語句:
在這裏插入圖片描述
上面程序中粗體字代碼分別聲明、初始化了兩個 IO 流,由於 BufferedReader、PrintStream 都實現了 Closeable 接口,而且它們放在 try 語句中聲明、初始化,所以 try 語句會自動關閉它們。因此上面程序是安全的。
自動關閉資源的 try 語句相當於包含了隱式的 finally 塊(這個 finally 塊用於關閉資源),因此這個 try 語句可以既沒有 catch 塊,也沒有 finally 塊。
在這裏插入圖片描述
如果程序需要,自動關閉資源的 try 語句後也可以帶多個 catch 塊和一個 finally 塊。