Java异常处理实战:5个提升代码健壮性的关键技巧
导语
异常处理是Java开发的核心技能,却常被忽视。本文深入解析异常处理的5个关键技巧,涵盖资源管理、日志记录和自定义异常等场景,附可直接复用的代码模板,帮助您编写更健壮可维护的应用程序。
一、try-with-resources的正确用法
场景:安全处理文件、网络连接等资源
传统方式的隐患:
FileInputStream fis = null;
try {
fis = new FileInputStream("data.txt");
// 处理文件...
} catch (IOException e) {
log.error("文件错误", e);
} finally {
try {
if (fis != null) fis.close(); // 可能忘记或关闭失败
} catch (IOException e) {
// 关闭异常被吞没
}
}
现代最佳实践:
try (var fis = new FileInputStream("data.txt");
var gzip = new GZIPInputStream(fis)) {
// 自动管理资源
processStream(gzip);
} catch (IOException e) {
log.error("文件处理失败: {}", e.getMessage(), e);
// 添加额外处理逻辑
notifyAdmin("文件处理异常", e);
}
优势分析:
- 自动关闭资源,避免泄漏
- 异常堆栈完整保留
- 代码简洁度提升50%
二、异常日志记录的黄金法则
常见错误:日志信息不足或过度记录
反模式示例:
try {
processOrder();
} catch (Exception e) {
log.error("处理失败"); // 缺少关键信息
// 或
log.error(e); // 没有自定义消息
}
最佳实践方案:
try {
processOrder(orderId);
} catch (PaymentException e) {
log.error("支付失败 [订单ID: {}, 用户: {}]", orderId, userId, e);
metrics.paymentFailureCounter.increment();
} catch (InventoryException e) {
log.warn("库存不足 [商品: {}]", productId, e);
notifyWarehouse(productId);
} catch (Exception e) {
log.error("未知错误 [订单: {}]", orderId, e);
reportToSentry(e);
}
日志原则:
- 包含业务关键参数(订单ID等)
- 区分异常级别(ERROR/WARN)
- 保留完整堆栈信息
三、自定义异常的设计技巧
场景:准确表达领域特定错误
错误设计:
throw new Exception("库存不足"); // 过于通用
正确实现:
// 领域特定异常
public class InventoryException extends RuntimeException {
private final String productId;
private final int requested;
private final int available;
public InventoryException(String productId, int requested, int available) {
super(String.format("商品 %s 库存不足 (请求: %d, 可用: %d)",
productId, requested, available));
this.productId = productId;
this.requested = requested;
this.available = available;
}
// 提供恢复方法
public int getAvailableQuantity() {
return available;
}
}
// 使用示例
if (available < requested) {
throw new InventoryException(productId, requested, available);
}
设计要点:
- 继承适当的父类(RuntimeException/Exception)
- 包含恢复所需的业务数据
- 提供清晰的错误消息
四、异常转换的最佳实践
场景:整合第三方库时统一异常体系
问题示例:
try {
thirdPartyService.process();
} catch (ThirdPartyException e) {
throw new RuntimeException(e); // 信息丢失
}
正确转换模式:
try {
thirdPartyService.process();
} catch (ThirdPartyTimeoutException e) {
throw new ServiceUnavailableException("服务响应超时", e);
} catch (ThirdPartyAuthException e) {
throw new AuthenticationException("认证失败: " + e.getReason(), e);
} catch (ThirdPartyException e) {
throw new BusinessException("第三方服务错误", e);
}
转换原则:
- 转换为领域相关异常
- 保留原始异常原因
- 补充有意义的上下文信息
五、全局异常处理机制
场景:Web应用统一异常处理
Spring Boot实现方案:
@RestControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(BusinessException.class)
@ResponseStatus(HttpStatus.BAD_REQUEST)
public ErrorResponse handleBusinessException(BusinessException ex) {
return new ErrorResponse("BUSINESS_ERROR", ex.getMessage());
}
@ExceptionHandler(ResourceNotFoundException.class)
@ResponseStatus(HttpStatus.NOT_FOUND)
public ErrorResponse handleNotFound(ResourceNotFoundException ex) {
return new ErrorResponse("NOT_FOUND", ex.getMessage());
}
@ExceptionHandler(Exception.class)
@ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
public ErrorResponse handleUnexpected(Exception ex) {
log.error("未处理异常", ex);
return new ErrorResponse("SERVER_ERROR", "系统内部错误");
}
}
// 统一错误响应格式
public record ErrorResponse(String code, String message) {}
优势:
- 统一API错误格式
- 分离异常处理与业务逻辑
- 避免重复的try-catch块