目錄
- 🚀前言
- 🤔異常的定義與分類
- 💯運行時異常
- 💯編譯時異常
- 💯異常的基本處理
- 🌟異常的作用
- 🐧自定義異常
- 💯自定義運行時異常
- 💯自定義編譯時異常
- ??異常的處理方案
- 💯方案一:異常捕獲與用戶友好提示
- 💯方案二:異常捕獲與自動修復
🚀前言
大家好!我是 EnigmaCoder。
本文介紹java的異常部分,從基礎到自定義,最后到處理方案進行全解析。
🤔異常的定義與分類
定義:Java
中的異常是程序運行時發生的錯誤或意外情況,是Throwable
類的子類,用于封裝程序執行過程中出現的不正常事件,可通過異常處理機制進行捕獲和處理,分為可處理的Exception
(包括受檢查異常和運行時異常)和不可處理的Error
(系統級錯誤)。
異常的分類
Java
異常體系基于Throwable
類,主要分為兩大類:
Error
:系統級錯誤(如內存溢出OutOfMemoryError
),程序無法處理,也就是說系統一旦出現問題,sun
公司會把這些問題封裝成Error
對象給出來。Exception
:指可被程序捕獲和處理的異常,我們程序員通常會用Exception
以及它的孩子來封裝程序出現的問題,其進一步分為:- 運行時異常(
RuntimeException
):RuntimeException
及其子類,編譯階段不會出現錯誤提示,運行時出現的異常。(如:數組索引越界異常) - 編譯時異常(
Checked Exception
):編譯階段會出現錯誤提示。(如:日期解析異常)
- 運行時異常(
💯運行時異常
特點:編譯階段不會報錯,運行時出現的異常,繼承自RuntimeException
類。
示例:
public class Text {public static void main(String[] args) {int []arr={1,2,3,4,5};System.out.println(arr[5]);}
}
這是一段數組越界的的代碼,編譯時沒有報錯,運行時就會出現以下報錯:
💯編譯時異常
public class Text {public static void main(String[] args) {show();}public static void show(){System.out.println("==程序開始==");String str="2025-01-01 00:00:00";SimpleDateFormat sdf=new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");Date date=sdf.parse(str);System.out.println(date);System.out.println("==程序結束==");}
}
報錯信息如下:
💯異常的基本處理
-
拋出異常(
throws
)在方法上使用
throws
關鍵字,可以將方法內部出現的異常拋出去給調用者處理。
格式如下:
方法 throws 異常1,異常2,異常3 ...{...
}
-
捕獲異常(
try...catch
)直接捕獲程序出現的異常。
格式如下:
try{//監視可能出現異常的代碼
}catch (異常類型1 變量){//處理異常
}catch (異常類型2 變量){//處理異常
}...
🌟異常的作用
- 作用1:異常是用來定位程序bug的關鍵信息
異常是程序運行過程中發生錯誤或異常情況時拋出的信號。它包含了豐富的調試信息,可以幫助開發者快速定位和修復問題。具體來說:
- 異常類型:指明錯誤的性質,如
NullPointerException
、ArrayIndexOutOfBoundsException
等。 - 異常信息:描述錯誤的具體原因,如"
Index 5 out of bounds for length 3
"。 - 堆棧跟蹤:顯示異常發生時的調用鏈,幫助定位問題發生的具體位置。
try {int[] arr = new int[3];System.out.println(arr[5]);
} catch (ArrayIndexOutOfBoundsException e) {e.printStackTrace();// 輸出:// java.lang.ArrayIndexOutOfBoundsException: Index 5 out of bounds for length 3// at Main.main(Main.java:5)
}
通過分析異常信息,開發者可以快速定位到數組越界的問題發生在
Main.java
文件的第5行。
- 作用2:可以作為方法內部的一種特殊返回值以便通知上層調用者,方法的執行問題
在方法設計中,異常機制提供了一種優雅的錯誤處理方式,相比傳統的錯誤碼返回具有以下優勢:
- 強制處理:調用者必須處理或繼續拋出檢查型異常(Checked Exception)。
- 信息豐富:可以攜帶詳細的錯誤描述和上下文信息。
- 代碼解耦:將正常業務邏輯與錯誤處理邏輯分離,提高代碼可讀性。
應用場景示例:
public void transfer(Account from, Account to, double amount) throws InsufficientBalanceException {if (from.getBalance() < amount) {throw new InsufficientBalanceException("賬戶余額不足,當前余額:" + from.getBalance());}// 執行轉賬邏輯
}// 調用方法
try {transfer(accountA, accountB, 1000);
} catch (InsufficientBalanceException e) {System.out.println("轉賬失敗:" + e.getMessage());// 輸出:轉賬失敗:賬戶余額不足,當前余額:500.0
}
通過拋出異常,
transfer
方法清晰地表達了業務約束,調用方可以針對性地處理特定錯誤情況,而不是依賴模糊的錯誤碼或布爾返回值。
🐧自定義異常
java
無法為這個世界上全部的問題都提供異常類來代表,如果企業自己的某種問題,想通過異常來表示,以便用異常來管理該問題,那就需要自己來定義異常類了。
💯自定義運行時異常
- 定義一個異常類繼承
RuntimeException
。
public class CustomRuntimeException extends RuntimeException {// 類體
}
- 重寫構造器
public class CustomRuntimeException extends RuntimeException {// 無參構造器public CustomRuntimeException() {super();}// 帶消息的構造器public CustomRuntimeException(String message) {super(message);}// 帶消息和原因的構造器public CustomRuntimeException(String message, Throwable cause) {super(message, cause);}// 帶原因的構造器public CustomRuntimeException(Throwable cause) {super(cause);}
}
- 通過
throw new
異常類(xxx)來創建異常對象并拋出。
public void someMethod(int value) {if (value < 0) {throw new CustomRuntimeException("Value cannot be negative");}// 其他邏輯
}
特點:編譯階段不報錯,運行時才可能出現。
💯自定義編譯時異常
- 定義一個異常類繼承
Exception
。
public class CustomCompileException extends Exception {// 異常類的具體實現
}
- 重寫構造器。
public class CustomCompileException extends Exception {// 無參構造器public CustomCompileException() {super();}// 帶有錯誤信息的構造器public CustomCompileException(String message) {super(message);}// 帶有錯誤信息和原因的構造器public CustomCompileException(String message, Throwable cause) {super(message, cause);}// 僅帶有原因的構造器public CustomCompileException(Throwable cause) {super(cause);}
}
- 通過
throw new
異常類(xxx)創建異常對象并拋出。
public void validateInput(String input) throws CustomCompileException {if (input == null || input.isEmpty()) {throw new CustomCompileException("輸入不能為空");}// 其他邏輯
}
特點:編譯階段就報錯,提醒十分激進。
??異常的處理方案
💯方案一:異常捕獲與用戶友好提示
底層異常層層往上拋出,最外層捕獲異常,記錄下異常信息,并響應適合用戶觀看的信息進行提示。
處理流程:
-
異常拋出:在底層代碼中,當檢測到異常情況時,使用
throw
關鍵字拋出異常對象。例如,在數據庫操作中,如果查詢失敗,可以拋出SQLException
。 -
異常傳遞:異常會沿著調用棧向上傳遞,直到被捕獲。中間層代碼可以選擇不處理異常,繼續向上拋出。例如,在服務層捕獲
SQLException
后,可以將其包裝為自定義的業務異常BusinessException
并繼續拋出。 -
最外層捕獲:在應用的最外層(如控制器層或主函數)使用
try-catch
塊捕獲所有異常。例如,在Spring MVC
的控制器中,可以使用@ExceptionHandler
注解來統一處理異常。 -
記錄日志:捕獲異常后,使用日志框架(如
Log4j
、SLF4J
)記錄異常的詳細信息,包括異常類型、堆棧軌跡、發生時間等。這有助于后續的問題排查和分析。 -
用戶提示:根據異常類型,生成適合用戶觀看的提示信息。例如,對于網絡超時異常,可以提示“網絡連接不穩定,請稍后重試”;對于權限不足異常,可以提示“您沒有權限執行此操作”。
示例代碼:
try {// 業務邏輯代碼
} catch (BusinessException e) {logger.error("業務異常: ", e);return new Response("操作失敗,請檢查輸入是否正確");
} catch (Exception e) {logger.error("系統異常: ", e);return new Response("系統繁忙,請稍后重試");
}
應用場景:
- Web 應用中的全局異常處理
- 微服務架構中的服務間調用異常處理
- 桌面應用程序中的用戶操作異常處理
💯方案二:異常捕獲與自動修復
最外層捕獲異常后,嘗試重新修復。
處理流程:
-
異常捕獲:在最外層代碼中捕獲異常,與方案一類似。
-
異常分析:根據異常類型和上下文信息,判斷是否可以進行自動修復。例如,對于網絡連接異常,可以嘗試重新連接;對于文件讀取異常,可以嘗試從備份文件讀取。
-
修復嘗試:
- 重試機制:對于暫時性異常(如網絡抖動),可以設置重試次數和間隔時間,多次嘗試執行操作。
- 備用方案:對于無法直接修復的異常,可以切換到備用方案。例如,主數據庫不可用時,切換到備用數據庫。
- 資源清理:在修復過程中,可能需要釋放已占用的資源,如關閉文件句柄、釋放數據庫連接等。
-
結果處理:
- 如果修復成功,繼續正常執行后續邏輯。
- 如果修復失敗,記錄日志并按照方案一的方式向用戶提示錯誤信息。
示例代碼:
int retryCount = 3;
while (retryCount > 0) {try {// 可能失敗的操作performOperation();break; // 成功則退出循環} catch (NetworkException e) {retryCount--;if (retryCount == 0) {logger.error("網絡連接失敗,重試次數用盡");return new Response("網絡連接失敗,請檢查網絡設置");}Thread.sleep(1000); // 等待1秒后重試}
}
應用場景:
- 分布式系統中的容錯處理
- 實時數據處理系統的故障恢復
- 自動化運維系統中的錯誤自愈
注意事項:
- 自動修復可能會掩蓋潛在的系統問題,應謹慎使用
- 需要設置合理的重試次數和間隔時間,避免無限重試
- 對于關鍵業務操作,建議在自動修復后仍進行人工確認
通過以上兩種方案,可以有效地處理系統中的異常情況,提高系統的穩定性和用戶體驗。具體選擇哪種方案,需要根據業務場景和異常類型來決定。