Skip to content

异常处理常用写法 ​

异常处理是保证程序健壮性的重要手段。本文详细介绍异常处理的最佳实践和常用写法,包含大量实用示例。

📋 目录 ​

🎯 一、异常捕获 ​

1.1 基本异常捕获 ​

try-catch块:

java
// 基本捕获
try {
    int result = 10 / 0;
} catch (ArithmeticException e) {
    System.out.println("除以零错误: " + e.getMessage());
}

// 捕获多个异常
try {
    // 可能抛出多种异常的代码
    processData();
} catch (NullPointerException e) {
    System.out.println("空指针异常: " + e.getMessage());
} catch (IllegalArgumentException e) {
    System.out.println("参数异常: " + e.getMessage());
} catch (Exception e) {
    System.out.println("其他异常: " + e.getMessage());
}

多重捕获(Java 7+):

java
// 捕获多个同类型异常
try {
    // 代码
} catch (NullPointerException | IllegalArgumentException e) {
    System.out.println("异常: " + e.getMessage());
    // 注意:多重捕获中,异常变量是final的
}

// 捕获多个不同类型异常
try {
    // 代码
} catch (IOException | SQLException e) {
    // 处理IO和SQL异常
    log.error("操作失败", e);
}

1.2 try-with-resources ​

资源自动关闭:

java
// 自动关闭资源(Java 7+)
try (FileInputStream fis = new FileInputStream("file.txt");
     BufferedReader reader = new BufferedReader(new InputStreamReader(fis))) {
    String line;
    while ((line = reader.readLine()) != null) {
        System.out.println(line);
    }
} catch (IOException e) {
    System.out.println("读取文件失败: " + e.getMessage());
}
// 资源会自动关闭,无需finally块

// 自定义资源
try (MyResource resource = new MyResource()) {
    resource.doSomething();
} catch (Exception e) {
    // 处理异常
}
// MyResource必须实现AutoCloseable接口

// 多个资源
try (
    FileInputStream fis1 = new FileInputStream("file1.txt");
    FileInputStream fis2 = new FileInputStream("file2.txt")
) {
    // 使用资源
}

1.3 finally块 ​

资源清理:

java
// 使用finally清理资源
FileInputStream fis = null;
try {
    fis = new FileInputStream("file.txt");
    // 使用流
} catch (IOException e) {
    System.out.println("读取失败: " + e.getMessage());
} finally {
    // 确保资源被关闭
    if (fis != null) {
        try {
            fis.close();
        } catch (IOException e) {
            System.out.println("关闭流失败: " + e.getMessage());
        }
    }
}

// finally中的return
public int test() {
    try {
        return 1;
    } finally {
        return 2;  // 最终返回2(不推荐这样做)
    }
}

🎯 二、自定义异常 ​

2.1 业务异常 ​

自定义业务异常:

java
// 业务异常基类
public class BusinessException extends RuntimeException {
    private String errorCode;
    private String errorMessage;
    
    public BusinessException(String errorCode, String errorMessage) {
        super(errorMessage);
        this.errorCode = errorCode;
        this.errorMessage = errorMessage;
    }
    
    public BusinessException(String errorCode, String errorMessage, Throwable cause) {
        super(errorMessage, cause);
        this.errorCode = errorCode;
        this.errorMessage = errorMessage;
    }
    
    // getter和setter
    public String getErrorCode() {
        return errorCode;
    }
    
    public String getErrorMessage() {
        return errorMessage;
    }
}

// 具体业务异常
public class UserNotFoundException extends BusinessException {
    public UserNotFoundException(Long userId) {
        super("USER_NOT_FOUND", "用户不存在: " + userId);
    }
}

public class InsufficientBalanceException extends BusinessException {
    public InsufficientBalanceException(String message) {
        super("INSUFFICIENT_BALANCE", message);
    }
}

2.2 系统异常 ​

系统异常封装:

java
// 系统异常
public class SystemException extends RuntimeException {
    private String errorCode;
    
    public SystemException(String errorCode, String message) {
        super(message);
        this.errorCode = errorCode;
    }
    
    public SystemException(String errorCode, String message, Throwable cause) {
        super(message, cause);
        this.errorCode = errorCode;
    }
}

// 使用示例
public void processFile(String fileName) {
    try {
        // 文件处理
    } catch (IOException e) {
        throw new SystemException("FILE_PROCESS_ERROR", 
            "文件处理失败: " + fileName, e);
    }
}

3. 异常使用示例 ​

业务场景使用:

java
public class UserService {
    
    public User getUserById(Long userId) {
        User user = userRepository.findById(userId);
        if (user == null) {
            throw new UserNotFoundException(userId);
        }
        return user;
    }
    
    public void transfer(Long fromUserId, Long toUserId, BigDecimal amount) {
        User fromUser = getUserById(fromUserId);
        User toUser = getUserById(toUserId);
        
        if (fromUser.getBalance().compareTo(amount) < 0) {
            throw new InsufficientBalanceException(
                "余额不足,当前余额: " + fromUser.getBalance()
            );
        }
        
        // 执行转账
        fromUser.setBalance(fromUser.getBalance().subtract(amount));
        toUser.setBalance(toUser.getBalance().add(amount));
    }
}

🎯 三、异常链 ​

3.1 异常传播 ​

异常重新抛出:

java
// 重新抛出异常
public void method1() throws IOException {
    try {
        method2();
    } catch (FileNotFoundException e) {
        // 记录日志
        log.error("文件未找到", e);
        // 重新抛出
        throw e;
    }
}

// 包装异常
public void method2() {
    try {
        // 可能抛出SQLException
        databaseOperation();
    } catch (SQLException e) {
        // 包装为运行时异常
        throw new RuntimeException("数据库操作失败", e);
    }
}

3.2 异常转换 ​

异常类型转换:

java
// 检查异常转运行时异常
public void process() {
    try {
        // 可能抛出检查异常
        fileOperation();
    } catch (IOException e) {
        throw new RuntimeException("文件操作失败", e);
    }
}

// 自定义异常转换
public void businessOperation() {
    try {
        // 数据库操作
        databaseOperation();
    } catch (SQLException e) {
        if (e.getErrorCode() == 1062) {
            // 唯一约束冲突
            throw new BusinessException("DUPLICATE_ENTRY", "记录已存在", e);
        } else {
            throw new SystemException("DATABASE_ERROR", "数据库操作失败", e);
        }
    }
}

🎯 四、异常日志 ​

4.1 异常日志记录 ​

日志记录最佳实践:

java
// 使用日志框架记录异常
public void processData() {
    try {
        // 业务逻辑
        doProcess();
    } catch (BusinessException e) {
        // 业务异常,记录警告日志
        log.warn("业务处理失败: {}", e.getMessage(), e);
        throw e;
    } catch (Exception e) {
        // 系统异常,记录错误日志
        log.error("系统处理失败", e);
        throw new SystemException("PROCESS_ERROR", "处理失败", e);
    }
}

// 异常信息记录
public void logException(Exception e, String context) {
    log.error("异常上下文: {}", context, e);
    log.error("异常类型: {}", e.getClass().getName());
    log.error("异常消息: {}", e.getMessage());
    log.error("堆栈跟踪:", e);
    
    // 获取根原因
    Throwable rootCause = getRootCause(e);
    if (rootCause != null) {
        log.error("根原因: {}", rootCause.getMessage());
    }
}

// 获取根原因
private Throwable getRootCause(Throwable throwable) {
    Throwable rootCause = throwable;
    while (rootCause.getCause() != null) {
        rootCause = rootCause.getCause();
    }
    return rootCause;
}

4.2 异常信息收集 ​

收集异常信息:

java
// 收集异常详细信息
public String collectExceptionInfo(Exception e) {
    StringBuilder sb = new StringBuilder();
    sb.append("异常类型: ").append(e.getClass().getName()).append("\n");
    sb.append("异常消息: ").append(e.getMessage()).append("\n");
    sb.append("异常堆栈:\n");
    
    // 堆栈跟踪
    StackTraceElement[] stackTrace = e.getStackTrace();
    for (int i = 0; i < Math.min(10, stackTrace.length); i++) {
        sb.append("  ").append(stackTrace[i]).append("\n");
    }
    
    // 原因链
    Throwable cause = e.getCause();
    if (cause != null) {
        sb.append("原因: ").append(cause.getMessage()).append("\n");
    }
    
    return sb.toString();
}

🎯 五、异常处理最佳实践 ​

5.1 异常处理原则 ​

基本原则:

java
// ✅ 好的做法:捕获具体异常
try {
    processFile();
} catch (FileNotFoundException e) {
    // 处理文件不存在
} catch (IOException e) {
    // 处理IO异常
}

// ❌ 不好的做法:捕获所有异常
try {
    processFile();
} catch (Exception e) {
    // 太宽泛,难以针对性处理
}

// ✅ 好的做法:记录异常信息
try {
    processData();
} catch (Exception e) {
    log.error("处理数据失败", e);
    throw e;  // 重新抛出
}

// ❌ 不好的做法:吞掉异常
try {
    processData();
} catch (Exception e) {
    // 什么都不做,异常被吞掉
}

5.2 异常处理模式 ​

统一异常处理:

java
// Controller层统一异常处理
@ControllerAdvice
public class GlobalExceptionHandler {
    
    // 处理业务异常
    @ExceptionHandler(BusinessException.class)
    public ResponseEntity<ErrorResponse> handleBusinessException(BusinessException e) {
        ErrorResponse error = new ErrorResponse(e.getErrorCode(), e.getErrorMessage());
        return ResponseEntity.badRequest().body(error);
    }
    
    // 处理参数异常
    @ExceptionHandler(IllegalArgumentException.class)
    public ResponseEntity<ErrorResponse> handleIllegalArgument(IllegalArgumentException e) {
        ErrorResponse error = new ErrorResponse("INVALID_PARAM", e.getMessage());
        return ResponseEntity.badRequest().body(error);
    }
    
    // 处理系统异常
    @ExceptionHandler(Exception.class)
    public ResponseEntity<ErrorResponse> handleException(Exception e) {
        log.error("系统异常", e);
        ErrorResponse error = new ErrorResponse("SYSTEM_ERROR", "系统错误");
        return ResponseEntity.status(500).body(error);
    }
}

返回值包装:

java
// 统一返回结果
public class Result<T> {
    private boolean success;
    private String code;
    private String message;
    private T data;
    
    // 成功结果
    public static <T> Result<T> success(T data) {
        Result<T> result = new Result<>();
        result.success = true;
        result.data = data;
        return result;
    }
    
    // 失败结果
    public static <T> Result<T> failure(String code, String message) {
        Result<T> result = new Result<>();
        result.success = false;
        result.code = code;
        result.message = message;
        return result;
    }
}

// 使用示例
public Result<User> getUser(Long id) {
    try {
        User user = userService.getUserById(id);
        return Result.success(user);
    } catch (UserNotFoundException e) {
        return Result.failure("USER_NOT_FOUND", e.getMessage());
    } catch (Exception e) {
        log.error("获取用户失败", e);
        return Result.failure("SYSTEM_ERROR", "系统错误");
    }
}

📖 学习资源 ​

官方文档 ​

书籍推荐 ​

  • 《Effective Java》 - Joshua Bloch

✅ 学习检查清单 ​

  • [ ] 已掌握异常捕获方法
  • [ ] 已学会自定义异常
  • [ ] 已理解异常链的使用
  • [ ] 已掌握异常日志记录
  • [ ] 已了解异常处理最佳实践

上一章节:日期时间处理常用写法
下一章节:IO操作常用写法
返回目录:代码规范