异常处理常用写法
异常处理是保证程序健壮性的重要手段。本文详细介绍异常处理的最佳实践和常用写法,包含大量实用示例。
📋 目录
🎯 一、异常捕获
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操作常用写法
返回目录:代码规范