Appearance
异常处理
异常处理是 Java 程序中处理错误和异常情况的重要机制。
异常概述
异常层次结构
Throwable
├── Error(错误)
│ ├── VirtualMachineError
│ ├── OutOfMemoryError
│ └── StackOverflowError
└── Exception(异常)
├── RuntimeException(运行时异常,非检查型)
│ ├── NullPointerException
│ ├── ArrayIndexOutOfBoundsException
│ ├── ArithmeticException
│ ├── ClassCastException
│ └── IllegalArgumentException
└── 其他异常(检查型异常)
├── IOException
├── SQLException
├── ClassNotFoundException
└── FileNotFoundException异常分类
- Error:JVM 无法解决的严重问题,如内存溢出
- 检查型异常:编译时必须处理的异常
- 非检查型异常:运行时异常,编译时不强制处理
异常处理
try-catch
java
try {
int result = 10 / 0;
} catch (ArithmeticException e) {
System.out.println("除零错误: " + e.getMessage());
}
// 多个 catch 块
try {
int[] arr = new int[5];
arr[10] = 50;
} catch (ArrayIndexOutOfBoundsException e) {
System.out.println("数组越界");
} catch (NullPointerException e) {
System.out.println("空指针");
} catch (Exception e) {
System.out.println("其他异常");
}
// Java 7+ 多异常捕获
try {
// 可能抛出多种异常的代码
} catch (IOException | SQLException e) {
System.out.println("IO 或 SQL 异常: " + e.getMessage());
}try-catch-finally
java
try {
// 可能出错的代码
FileReader file = new FileReader("test.txt");
} catch (FileNotFoundException e) {
System.out.println("文件未找到");
} finally {
// 无论是否异常都会执行
System.out.println("清理资源");
}
// finally 常用于资源清理
FileReader reader = null;
try {
reader = new FileReader("test.txt");
// 读取操作
} catch (IOException e) {
e.printStackTrace();
} finally {
if (reader != null) {
try {
reader.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}try-with-resources
java
// Java 7+ 自动资源管理
try (FileReader reader = new FileReader("test.txt");
BufferedReader br = new BufferedReader(reader)) {
String line = br.readLine();
} catch (IOException e) {
e.printStackTrace();
}
// 资源自动关闭,无需 finally
// 自定义资源类需要实现 AutoCloseable
class MyResource implements AutoCloseable {
public void doSomething() {
System.out.println("使用资源");
}
@Override
public void close() {
System.out.println("关闭资源");
}
}
try (MyResource resource = new MyResource()) {
resource.doSomething();
}throw 关键字
java
public void setAge(int age) {
if (age < 0 || age > 150) {
throw new IllegalArgumentException("年龄必须在 0-150 之间");
}
this.age = age;
}
// 抛出自定义异常
public void withdraw(double amount) throws InsufficientFundsException {
if (amount > balance) {
throw new InsufficientFundsException("余额不足");
}
balance -= amount;
}throws 关键字
java
// 声明方法可能抛出的异常
public void readFile(String path) throws IOException, FileNotFoundException {
FileReader reader = new FileReader(path);
// ...
}
// 调用者必须处理或继续声明
public void processFile() {
try {
readFile("test.txt");
} catch (IOException e) {
e.printStackTrace();
}
}自定义异常
创建自定义异常
java
// 自定义检查型异常
public class InsufficientFundsException extends Exception {
private double balance;
private double amount;
public InsufficientFundsException(double balance, double amount) {
super("余额不足: 当前余额 " + balance + ", 取款金额 " + amount);
this.balance = balance;
this.amount = amount;
}
public double getBalance() { return balance; }
public double getAmount() { return amount; }
}
// 自定义运行时异常
public class InvalidAgeException extends RuntimeException {
public InvalidAgeException(String message) {
super(message);
}
public InvalidAgeException(int age) {
super("无效年龄: " + age);
}
}使用自定义异常
java
public class BankAccount {
private double balance;
public BankAccount(double balance) {
this.balance = balance;
}
public void withdraw(double amount) throws InsufficientFundsException {
if (amount <= 0) {
throw new IllegalArgumentException("取款金额必须大于0");
}
if (amount > balance) {
throw new InsufficientFundsException(balance, amount);
}
balance -= amount;
System.out.println("取款成功,余额: " + balance);
}
public void setAge(int age) {
if (age < 0 || age > 150) {
throw new InvalidAgeException(age);
}
this.age = age;
}
}
// 使用
public class Main {
public static void main(String[] args) {
BankAccount account = new BankAccount(1000);
try {
account.withdraw(1500);
} catch (InsufficientFundsException e) {
System.out.println(e.getMessage());
System.out.println("当前余额: " + e.getBalance());
}
}
}异常链
java
public class DataProcessor {
public void process(String data) throws DataProcessingException {
try {
// 解析数据
int value = Integer.parseInt(data);
} catch (NumberFormatException e) {
// 保留原始异常信息
throw new DataProcessingException("数据解析失败", e);
}
}
}
public class DataProcessingException extends Exception {
public DataProcessingException(String message, Throwable cause) {
super(message, cause);
}
}
// 使用
try {
processor.process("abc");
} catch (DataProcessingException e) {
System.out.println(e.getMessage());
System.out.println("原因: " + e.getCause());
}异常最佳实践
1. 只捕获能处理的异常
java
// 不推荐:捕获所有异常
try {
// 很多操作
} catch (Exception e) {
e.printStackTrace();
}
// 推荐:只捕获特定异常
try {
fileReader = new FileReader(path);
} catch (FileNotFoundException e) {
System.out.println("文件不存在: " + path);
}2. 不要忽略异常
java
// 不推荐
try {
doSomething();
} catch (Exception e) {
// 空的 catch 块
}
// 推荐:至少记录日志
try {
doSomething();
} catch (Exception e) {
logger.error("操作失败", e);
// 或者重新抛出
throw new RuntimeException("操作失败", e);
}3. 使用具体的异常类型
java
// 不推荐
public void validate(String input) throws Exception {
// ...
}
// 推荐
public void validate(String input) throws ValidationException {
// ...
}4. 提供有意义的异常信息
java
// 不推荐
throw new IllegalArgumentException("Invalid input");
// 推荐
throw new IllegalArgumentException("用户名不能为空");5. 尽早失败
java
public void transfer(BankAccount from, BankAccount to, double amount) {
// 先检查所有条件
if (from == null) {
throw new IllegalArgumentException("源账户不能为空");
}
if (to == null) {
throw new IllegalArgumentException("目标账户不能为空");
}
if (amount <= 0) {
throw new IllegalArgumentException("转账金额必须大于0");
}
// 再执行操作
from.withdraw(amount);
to.deposit(amount);
}6. 合理使用检查型和非检查型异常
java
// 检查型异常:可恢复的情况
public void readFile(String path) throws FileNotFoundException {
// 文件不存在是可以恢复的,用户可以提供新路径
}
// 非检查型异常:编程错误
public void setAge(int age) {
if (age < 0) {
throw new IllegalArgumentException("年龄不能为负数");
// 这是编程错误,不应该捕获
}
}常见异常
NullPointerException
java
String str = null;
// str.length(); // NullPointerException
// 预防
if (str != null) {
System.out.println(str.length());
}
// 使用 Objects.requireNonNull
public void setName(String name) {
this.name = Objects.requireNonNull(name, "姓名不能为空");
}
// Java 8+ Optional
Optional.ofNullable(str)
.ifPresent(s -> System.out.println(s.length()));ArrayIndexOutOfBoundsException
java
int[] arr = new int[5];
// arr[5] = 10; // ArrayIndexOutOfBoundsException
// 预防
int index = 5;
if (index >= 0 && index < arr.length) {
arr[index] = 10;
}ClassCastException
java
Object obj = "Hello";
// Integer num = (Integer) obj; // ClassCastException
// 预防
if (obj instanceof Integer) {
Integer num = (Integer) obj;
}NumberFormatException
java
// Integer.parseInt("abc"); // NumberFormatException
// 预防
String input = "abc";
try {
int num = Integer.parseInt(input);
} catch (NumberFormatException e) {
System.out.println("无效的数字格式: " + input);
}
// 使用工具方法
public static Integer parseInteger(String str) {
try {
return Integer.parseInt(str);
} catch (NumberFormatException e) {
return null;
}
}实践示例
输入验证工具
java
import java.util.Objects;
public class ValidationUtils {
public static <T> T requireNonNull(T obj, String message) {
if (obj == null) {
throw new IllegalArgumentException(message);
}
return obj;
}
public static String requireNonEmpty(String str, String message) {
requireNonNull(str, message);
if (str.trim().isEmpty()) {
throw new IllegalArgumentException(message);
}
return str;
}
public static int requirePositive(int value, String message) {
if (value <= 0) {
throw new IllegalArgumentException(message);
}
return value;
}
public static int requireRange(int value, int min, int max, String message) {
if (value < min || value > max) {
throw new IllegalArgumentException(
message + " (范围: " + min + "-" + max + ")");
}
return value;
}
public static String requireEmail(String email) {
requireNonEmpty(email, "邮箱不能为空");
if (!email.matches("^[\\w.-]+@[\\w.-]+\\.[a-zA-Z]{2,}$")) {
throw new IllegalArgumentException("邮箱格式不正确");
}
return email;
}
public static String requirePhone(String phone) {
requireNonEmpty(phone, "手机号不能为空");
if (!phone.matches("^1[3-9]\\d{9}$")) {
throw new IllegalArgumentException("手机号格式不正确");
}
return phone;
}
}
// 使用
public class User {
private String name;
private String email;
private int age;
public User(String name, String email, int age) {
this.name = ValidationUtils.requireNonEmpty(name, "姓名不能为空");
this.email = ValidationUtils.requireEmail(email);
this.age = ValidationUtils.requireRange(age, 0, 150, "年龄无效");
}
}异常处理框架
java
import java.util.function.*;
public class ExceptionHandler {
// 处理可能抛出异常的 Supplier
public static <T> T handle(Supplier<T> supplier, T defaultValue) {
try {
return supplier.get();
} catch (Exception e) {
return defaultValue;
}
}
// 处理可能抛出异常的 Runnable
public static void handle(Runnable runnable) {
try {
runnable.run();
} catch (Exception e) {
// 静默处理
}
}
// 包装异常
public static <T> T wrap(Supplier<T> supplier,
Function<Exception, RuntimeException> wrapper) {
try {
return supplier.get();
} catch (Exception e) {
throw wrapper.apply(e);
}
}
// 重试机制
public static <T> T retry(Supplier<T> supplier, int maxRetries) {
Exception lastException = null;
for (int i = 0; i < maxRetries; i++) {
try {
return supplier.get();
} catch (Exception e) {
lastException = e;
}
}
throw new RuntimeException("重试" + maxRetries + "次后仍失败", lastException);
}
// 使用示例
public static void main(String[] args) {
// 安全解析整数
Integer num = handle(() -> Integer.parseInt("123"), 0);
System.out.println(num);
// 重试网络请求
String result = retry(() -> {
// 模拟网络请求
return "success";
}, 3);
System.out.println(result);
}
}业务异常处理
java
import java.util.*;
// 业务异常基类
public class BusinessException extends RuntimeException {
private final String code;
public BusinessException(String code, String message) {
super(message);
this.code = code;
}
public String getCode() { return code; }
}
// 具体业务异常
public class UserNotFoundException extends BusinessException {
public UserNotFoundException(Long userId) {
super("USER_NOT_FOUND", "用户不存在: " + userId);
}
}
public class InsufficientBalanceException extends BusinessException {
public InsufficientBalanceException(double balance, double amount) {
super("INSUFFICIENT_BALANCE",
"余额不足: 当前 " + balance + ", 需要 " + amount);
}
}
// 统一异常处理器
public class GlobalExceptionHandler {
public Map<String, Object> handle(Exception e) {
Map<String, Object> result = new HashMap<>();
if (e instanceof BusinessException) {
BusinessException be = (BusinessException) e;
result.put("success", false);
result.put("code", be.getCode());
result.put("message", be.getMessage());
} else {
result.put("success", false);
result.put("code", "SYSTEM_ERROR");
result.put("message", "系统错误");
}
return result;
}
}
// 使用
public class UserService {
private Map<Long, User> users = new HashMap<>();
public User getUser(Long userId) {
User user = users.get(userId);
if (user == null) {
throw new UserNotFoundException(userId);
}
return user;
}
}