深入理解 Java 异常处理机制
目录
1. 异常体系结构
2. 异常分类
2.1 Error(错误)
2.2 Exception(异常)
2.2.1 受检异常(Checked Exception)
2.2.2 非受检异常(Unchecked Exception)
3. 异常处理机制
3.1 try-catch-finally 块
3.2 try-with-resources(Java 7+)
3.3 多重catch块
4. 抛出异常
4.1 throw 关键字
4.2 throws 关键字
5. 自定义异常
6. 异常处理最佳实践
6.1 不要忽略异常
6.2 使用有意义的异常信息
6.3 异常链(Exception Chaining)
7. 常见的异常处理模式
7.1 重试机制
7.2 防御性编程
8. 总结
关键要点:
异常处理是 Java 编程中至关重要的部分,它帮助我们编写更健壮、更可靠的程序。本文将全面介绍 Java 异常处理的各个方面。
1. 异常体系结构
Java 的异常类都继承自 Throwable
类,主要分为两大体系:
Throwable├── Error (错误)│ ├── OutOfMemoryError│ ├── StackOverflowError│ └── VirtualMachineError│└── Exception (异常)├── RuntimeException (运行时异常/非受检异常)│ ├── NullPointerException│ ├── ArrayIndexOutOfBoundsException│ ├── IllegalArgumentException│ └── ArithmeticException│└── 其他Exception (受检异常)├── IOException├── SQLException├── FileNotFoundException└── ClassNotFoundException
2. 异常分类
2.1 Error(错误)
表示严重的系统级错误,程序通常无法处理这些错误。
public class ErrorExample {public static void main(String[] args) {// StackOverflowErrorrecursiveMethod(0);// OutOfMemoryError// List<Object> list = new ArrayList<>();// while (true) {// list.add(new Object());// }}private static void recursiveMethod(int i) {System.out.println(i);recursiveMethod(i + 1); // 导致栈溢出}
}
2.2 Exception(异常)
程序可以处理的异常情况,分为受检异常和非受检异常。
2.2.1 受检异常(Checked Exception)
编译器要求必须处理的异常。
public class CheckedExceptionExample {public void readFile() {try {FileReader file = new FileReader("nonexistent.txt");BufferedReader reader = new BufferedReader(file);String line = reader.readLine();System.out.println(line);reader.close();} catch (FileNotFoundException e) {System.out.println("文件未找到: " + e.getMessage());} catch (IOException e) {System.out.println("IO错误: " + e.getMessage());}}// 或者抛出异常public void readFile2() throws IOException {FileReader file = new FileReader("nonexistent.txt");BufferedReader reader = new BufferedReader(file);String line = reader.readLine();System.out.println(line);reader.close();}
}
2.2.2 非受检异常(Unchecked Exception)
编译器不要求强制处理的异常,通常是编程错误。
public class UncheckedExceptionExample {public void example() {// NullPointerExceptionString str = null;// System.out.println(str.length()); // 抛出NPE// ArrayIndexOutOfBoundsExceptionint[] arr = new int[5];// System.out.println(arr[10]); // 抛出AI0OBE// ArithmeticExceptionint a = 10;int b = 0;// System.out.println(a / b); // 抛出AE// IllegalArgumentExceptionsetAge(-5); // 可能抛出IAE}public void setAge(int age) {if (age < 0) {throw new IllegalArgumentException("年龄不能为负数: " + age);}// 设置年龄逻辑}
}
3. 异常处理机制
3.1 try-catch-finally 块
public class ExceptionHandling {public void processFile(String filename) {FileReader reader = null;try {reader = new FileReader(filename);// 读取文件内容int data = reader.read();while (data != -1) {System.out.print((char) data);data = reader.read();}} catch (FileNotFoundException e) {System.err.println("文件不存在: " + e.getMessage());} catch (IOException e) {System.err.println("读取文件时发生IO错误: " + e.getMessage());} finally {// 无论是否发生异常都会执行try {if (reader != null) {reader.close();}} catch (IOException e) {System.err.println("关闭文件时发生错误: " + e.getMessage());}}}
}
3.2 try-with-resources(Java 7+)
自动资源管理,简化资源关闭操作。
public class TryWithResourcesExample {public void readFile(String filename) {try (FileReader reader = new FileReader(filename);BufferedReader bufferedReader = new BufferedReader(reader)) {String line;while ((line = bufferedReader.readLine()) != null) {System.out.println(line);}} catch (FileNotFoundException e) {System.err.println("文件未找到: " + e.getMessage());} catch (IOException e) {System.err.println("IO错误: " + e.getMessage());}// 资源会自动关闭,无需finally块}
}
3.3 多重catch块
public class MultiCatchExample {public void processInput(String input) {try {int number = Integer.parseInt(input);System.out.println("数字: " + number);} catch (NumberFormatException e) {System.err.println("输入不是有效的数字: " + input);} catch (Exception e) {System.err.println("发生未知错误: " + e.getMessage());} finally {System.out.println("处理完成");}}// Java 7+ 多重异常捕获public void processData(String data) {try {// 可能抛出多种异常的操作parseAndValidate(data);} catch (NumberFormatException | IllegalArgumentException e) {System.err.println("数据格式错误: " + e.getMessage());} catch (IOException e) {System.err.println("IO错误: " + e.getMessage());}}
}
4. 抛出异常
4.1 throw 关键字
public class ThrowExample {public void withdraw(double amount) {if (amount <= 0) {throw new IllegalArgumentException("取款金额必须大于0");}if (amount > balance) {throw new InsufficientFundsException("余额不足", amount, balance);}balance -= amount;}private double balance = 1000;
}// 自定义异常
class InsufficientFundsException extends RuntimeException {private final double requestedAmount;private final double availableBalance;public InsufficientFundsException(String message, double requestedAmount, double availableBalance) {super(message);this.requestedAmount = requestedAmount;this.availableBalance = availableBalance;}public double getRequestedAmount() {return requestedAmount;}public double getAvailableBalance() {return availableBalance;}
}
4.2 throws 关键字
public class ThrowsExample {// 声明可能抛出的受检异常public void readConfigFile() throws IOException, FileNotFoundException {Properties props = new Properties();try (InputStream input = new FileInputStream("config.properties")) {props.load(input);}}// 调用者必须处理这些异常public void loadConfiguration() {try {readConfigFile();} catch (IOException e) {System.err.println("无法读取配置文件: " + e.getMessage());// 使用默认配置loadDefaultConfig();}}private void loadDefaultConfig() {// 加载默认配置}
}
5. 自定义异常
// 受检异常
public class DatabaseException extends Exception {private final String sqlState;private final int errorCode;public DatabaseException(String message, String sqlState, int errorCode) {super(message);this.sqlState = sqlState;this.errorCode = errorCode;}public DatabaseException(String message, String sqlState, int errorCode, Throwable cause) {super(message, cause);this.sqlState = sqlState;this.errorCode = errorCode;}public String getSqlState() {return sqlState;}public int getErrorCode() {return errorCode;}@Overridepublic String toString() {return String.format("DatabaseException[sqlState=%s, errorCode=%d]: %s", sqlState, errorCode, getMessage());}
}// 非受检异常
public class BusinessException extends RuntimeException {private final String errorCode;private final Map<String, Object> details;public BusinessException(String errorCode, String message) {super(message);this.errorCode = errorCode;this.details = new HashMap<>();}public BusinessException(String errorCode, String message, Map<String, Object> details) {super(message);this.errorCode = errorCode;this.details = details != null ? new HashMap<>(details) : new HashMap<>();}public String getErrorCode() {return errorCode;}public Map<String, Object> getDetails() {return Collections.unmodifiableMap(details);}public void addDetail(String key, Object value) {details.put(key, value);}
}
6. 异常处理最佳实践
6.1 不要忽略异常
public class BestPractices {// 错误的做法:忽略异常public void badPractice() {try {// 可能抛出异常的操作processData();} catch (Exception e) {// 空的catch块 - 绝对不要这样做!}}// 正确的做法:适当处理异常public void goodPractice() {try {processData();} catch (SpecificException e) {// 记录日志logger.error("处理数据时发生错误", e);// 恢复或重试retryOperation();} catch (AnotherException e) {// 转换异常类型throw new BusinessException("PROCESS_ERROR", "处理失败", e);}}
}
6.2 使用有意义的异常信息
public class MeaningfulExceptions {public void validateUser(User user) {if (user == null) {throw new IllegalArgumentException("用户对象不能为null");}if (user.getName() == null || user.getName().trim().isEmpty()) {throw new IllegalArgumentException("用户名不能为空");}if (user.getAge() < 0 || user.getAge() > 150) {throw new IllegalArgumentException("年龄必须在0-150之间: " + user.getAge());}}
}
6.3 异常链(Exception Chaining)
public class ExceptionChaining {public void processOrder(Order order) {try {validateOrder(order);calculateTotal(order);processPayment(order);} catch (ValidationException e) {throw new OrderProcessingException("订单验证失败", e);} catch (PaymentException e) {throw new OrderProcessingException("支付处理失败", e);} catch (Exception e) {throw new OrderProcessingException("处理订单时发生未知错误", e);}}
}
7. 常见的异常处理模式
7.1 重试机制
public class RetryPattern {private static final int MAX_RETRIES = 3;private static final long RETRY_DELAY = 1000; // 1秒public void executeWithRetry(Runnable operation) {int attempt = 0;while (attempt < MAX_RETRIES) {try {operation.run();return; // 成功则退出} catch (RetryableException e) {attempt++;if (attempt >= MAX_RETRIES) {throw new MaxRetriesExceededException("操作重试次数超过限制", e);}System.out.println("操作失败,第" + attempt + "次重试...");try {Thread.sleep(RETRY_DELAY);} catch (InterruptedException ie) {Thread.currentThread().interrupt();throw new OperationInterruptedException("操作被中断", ie);}}}}
}
7.2 防御性编程
public class DefensiveProgramming {public void processData(String data) {// 参数校验if (data == null) {throw new IllegalArgumentException("数据不能为null");}if (data.trim().isEmpty()) {throw new IllegalArgumentException("数据不能为空字符串");}try {// 业务逻辑doProcess(data);} catch (BusinessException e) {// 已知的业务异常handleBusinessException(e);} catch (Exception e) {// 未知异常,转换为业务异常throw new BusinessException("UNKNOWN_ERROR", "处理数据时发生未知错误", e);}}
}
8. 总结
关键要点:
- 了解异常体系:Error vs Exception,受检异常 vs 非受检异常
- 正确使用处理机制:try-catch-finally,try-with-resources
- 合理抛出异常:使用 throw 和 throws
- 创建有意义的异常:自定义异常类,提供有用的错误信息
- 遵循最佳实践:不要忽略异常,使用异常链,适当记录日志