????????在前六天的學習中,我們掌握了 Java 的基礎語法、面向對象核心特性、抽象類與接口等知識。今天我們將學習 Java 中的異常處理機制,這是保證程序健壯性的關鍵技術。在 JavaWeb 開發中,無論是用戶輸入錯誤、數據庫連接失敗還是網絡異常,都需要通過異常處理機制來優雅地處理,避免程序崩潰并給用戶友好提示。
什么是異常?
異常(Exception)是程序運行過程中發生的意外情況,它會中斷程序的正常執行流程。例如:
- 除以零(
ArithmeticException
) - 訪問數組越界(
ArrayIndexOutOfBoundsException
) - 讀取不存在的文件(
FileNotFoundException
) - 網絡連接中斷
沒有異常處理的程序遇到異常時會直接崩潰,例如:
public class TestWithoutException {public static void main(String[] args) {int a = 10;int b = 0;System.out.println(a / b); // 發生算術異常System.out.println("程序繼續執行"); // 這行代碼不會執行}
}
運行結果:
Exception in thread "main" java.lang.ArithmeticException: / by zeroat TestWithoutException.main(TestWithoutException.java:5)
異常的分類
Java 中的異常體系以Throwable
為根類,主要分為兩大類:
Error(錯誤):
- 由 JVM 產生的嚴重錯誤,程序無法處理(如內存溢出
OutOfMemoryError
) - 通常不需要捕獲,需要從代碼層面解決
- 由 JVM 產生的嚴重錯誤,程序無法處理(如內存溢出
Exception(異常):
- 程序可以處理的異常,分為:
- 編譯時異常(受檢異常):編譯期間必須處理的異常(如
IOException
、SQLException
) - 運行時異常(非受檢異常):運行時才會發生的異常(如
NullPointerException
、IndexOutOfBoundsException
)
- 編譯時異常(受檢異常):編譯期間必須處理的異常(如
- 程序可以處理的異常,分為:
異常體系結構簡圖:
Throwable
├─ Error(錯誤)
│ ├─ OutOfMemoryError
│ └─ StackOverflowError
│
└─ Exception(異常)├─ RuntimeException(運行時異常)│ ├─ NullPointerException│ ├─ ArithmeticException│ └─ IndexOutOfBoundsException│├─ IOException(編譯時異常)├─ SQLException(編譯時異常)└─ ClassNotFoundException(編譯時異常)
異常處理的核心語法
Java 提供了try-catch-finally
和throws
關鍵字來處理異常,確保程序在遇到異常時能夠繼續執行或優雅退出。
1. try-catch-finally 語句
try {// 可能發生異常的代碼塊
} catch (異常類型1 變量名1) {// 處理異常類型1的代碼
} catch (異常類型2 變量名2) {// 處理異常類型2的代碼
} finally {// 無論是否發生異常,都會執行的代碼(通常用于資源釋放)
}
執行流程:
- 如果
try
塊中沒有異常,執行try
塊后直接執行finally
塊 - 如果
try
塊中有異常,中斷try
塊執行,匹配對應的catch
塊處理,最后執行finally
塊
實例:
public class TryCatchDemo {public static void main(String[] args) {int[] nums = {1, 2, 3};try {// 可能發生異常的操作int result = 10 / 0; // 算術異常System.out.println(nums[3]); // 數組越界異常(不會執行)} catch (ArithmeticException e) {// 處理算術異常System.out.println("捕獲到算術異常:" + e.getMessage());e.printStackTrace(); // 打印異常堆棧信息(調試用)} catch (ArrayIndexOutOfBoundsException e) {// 處理數組越界異常System.out.println("捕獲到數組越界異常:" + e.getMessage());} finally {// 無論是否異常,都會執行System.out.println("finally塊執行:資源釋放操作");}// 異常處理后,程序可以繼續執行System.out.println("程序繼續運行...");}
}
運行結果:
捕獲到算術異常:/ by zero
java.lang.ArithmeticException: / by zeroat TryCatchDemo.main(TryCatchDemo.java:8)
finally塊執行:資源釋放操作
程序繼續運行...
2. throws 聲明異常
當方法內部無法處理異常時,可以使用throws
關鍵字聲明該方法可能拋出的異常,由調用者處理:
// 聲明方法可能拋出的異常
修飾符 返回值類型 方法名(參數列表) throws 異常類型1, 異常類型2 {// 可能拋出異常的代碼
}
實例:
import java.io.FileInputStream;
import java.io.FileNotFoundException;public class ThrowsDemo {// 聲明可能拋出編譯時異常public static void readFile(String fileName) throws FileNotFoundException {// 讀取文件(可能拋出FileNotFoundException)new FileInputStream(fileName);}public static void main(String[] args) {try {// 調用聲明異常的方法,必須處理異常readFile("test.txt");} catch (FileNotFoundException e) {System.out.println("處理文件不存在異常:" + e.getMessage());}}
}
注意:
- 運行時異常可以不聲明,但編譯時異常必須聲明或捕獲
- 子類重寫父類方法時,拋出的異常不能超過父類方法聲明的異常范圍
3. throw 主動拋出異常
使用throw
關鍵字可以在代碼中主動拋出異常:
public class ThrowDemo {// 驗證年齡的方法public static void checkAge(int age) {if (age < 0 || age > 120) {// 主動拋出異常throw new IllegalArgumentException("年齡不合法:" + age);}System.out.println("年齡驗證通過:" + age);}public static void main(String[] args) {try {checkAge(150); // 調用方法} catch (IllegalArgumentException e) {System.out.println("捕獲到異常:" + e.getMessage());}}
}
運行結果:
捕獲到異常:年齡不合法:150
自定義異常
在實際開發中,系統提供的異常可能無法滿足業務需求,這時可以自定義異常類:
- 繼承
Exception
(編譯時異常)或RuntimeException
(運行時異常) - 提供構造方法(通常需要帶消息的構造方法)
實例:
// 自定義編譯時異常
public class InsufficientFundsException extends Exception {// 無參構造public InsufficientFundsException() {super();}// 帶消息的構造public InsufficientFundsException(String message) {super(message);}
}// 自定義運行時異常
public class InvalidAccountException extends RuntimeException {public InvalidAccountException(String message) {super(message);}
}// 使用自定義異常
public class BankService {private double balance; // 賬戶余額public BankService(double balance) {this.balance = balance;}// 取款方法(可能拋出自定義異常)public void withdraw(double amount) throws InsufficientFundsException {if (amount <= 0) {throw new InvalidAccountException("取款金額不能為負數");}if (amount > balance) {// 拋出編譯時異常,必須聲明throw new InsufficientFundsException("余額不足,當前余額:" + balance + ",取款:" + amount);}balance -= amount;System.out.println("取款成功,剩余余額:" + balance);}public static void main(String[] args) {BankService bank = new BankService(1000);try {bank.withdraw(1500);} catch (InsufficientFundsException e) {System.out.println("取款失敗:" + e.getMessage());} catch (InvalidAccountException e) {System.out.println("操作失敗:" + e.getMessage());}}
}
運行結果:
取款失敗:余額不足,當前余額:1000.0,取款:1500.0
異常處理的最佳實踐
避免捕獲所有異常:不要使用
catch (Exception e)
捕獲所有異常,應該針對性處理// 不推薦 try {// ... } catch (Exception e) {// 無法區分具體異常類型 }
不要忽略異常:捕獲異常后必須處理(至少記錄日志),避免空的
catch
塊// 不推薦 try {// ... } catch (IOException e) {// 空塊會隱藏錯誤 }
釋放資源:使用
finally
塊釋放資源(如文件流、數據庫連接)FileInputStream fis = null; try {fis = new FileInputStream("test.txt");// 讀取文件 } catch (FileNotFoundException e) {e.printStackTrace(); } finally {// 確保關閉流if (fis != null) {try {fis.close();} catch (IOException e) {e.printStackTrace();}} }
Java 7+:try-with-resources:自動釋放實現
AutoCloseable
接口的資源// 推薦:自動關閉資源,無需手動調用close() try (FileInputStream fis = new FileInputStream("test.txt")) {// 讀取文件 } catch (IOException e) {e.printStackTrace(); }
使用恰當的異常類型:根據業務場景選擇或創建合適的異常類型
異常處理在 JavaWeb 中的應用
在 JavaWeb 開發中,異常處理尤為重要,常見場景包括:
Servlet 中的異常處理:
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {try {String username = request.getParameter("username");if (username == null || username.isEmpty()) {throw new IllegalArgumentException("用戶名不能為空");}// 處理業務邏輯} catch (IllegalArgumentException e) {// 向客戶端返回錯誤信息response.getWriter().write("錯誤:" + e.getMessage());response.setStatus(400); // 設置HTTP錯誤狀態碼} }
全局異常處理器:在 Spring 等框架中,可以定義全局異常處理器統一處理異常,避免代碼冗余
總結與實踐
知識點回顧
- 異常概念:程序運行時的意外情況,會中斷正常執行流程
- 異常分類:
Error
(無法處理)和Exception
(可處理),Exception
分為編譯時異常和運行時異常 - 處理方式:
try-catch-finally
:捕獲并處理異常,釋放資源throws
:聲明方法可能拋出的異常,由調用者處理throw
:主動拋出異常
- 自定義異常:繼承
Exception
或RuntimeException
,滿足業務需求 - 最佳實踐:針對性處理異常、不忽略異常、及時釋放資源
實踐任務
用戶注冊異常處理:
- 創建自定義異常
UserAlreadyExistsException
(用戶已存在)和InvalidUserInfoException
(用戶信息無效) - 編寫
UserService
類,包含register(String username, String password)
方法:- 如果用戶名已存在(可簡單判斷是否為 "admin"),拋出
UserAlreadyExistsException
- 如果密碼長度小于 6 位,拋出
InvalidUserInfoException
- 否則提示注冊成功
- 如果用戶名已存在(可簡單判斷是否為 "admin"),拋出
- 編寫測試類,使用
try-catch
處理異常并輸出相應信息
- 創建自定義異常
文件讀取異常處理:
- 編寫
FileUtil
類,包含readFileContent(String filePath)
方法,讀取文件內容 - 處理可能的異常(文件不存在、IO 異常等)
- 使用
try-with-resources
確保文件流正確關閉
- 編寫
思考:在 Web 開發中,為什么不建議直接將異常堆棧信息返回給客戶端?應該如何處理?