1. 主页 > 小妙招

Java中try-catch如何优雅处理异常?实战代码示例解析

哎我说,你们是不是经常看到别人的代码像穿了防弹衣,怎么折腾都不崩溃?再看看自己写的程序,动不动就给你甩脸色——NullPointerException、ArrayIndexOutOfBoundsException...(扶额)今天咱就唠唠这异常处理的??优雅之道??,保准让你的代码从玻璃心变钢铁侠!

(敲桌子)先整明白个事儿:??优雅处理异常就像吃螃蟹,得知道怎么拆壳又不浪费蟹黄??。光会try-catch可不够,关键要看姿势对不对!


第一回合:基础版vs优雅版对比实验

??场景??:用户登录时读取数据库
菜鸟写法

java复制
try {
    Connection conn = DriverManager.getConnection(url);
    // 执行SQL...
} catch (SQLException e) {
    e.printStackTrace();
}

这写法有啥问题?异常吃了不处理,就像吞了鱼刺不吐骨头——迟早卡嗓子眼!

老司机改造

java复制
try {
    Connection conn = DriverManager.getConnection(url);
    // 执行SQL...
} catch (SQLException e) {
    logger.error("数据库连接异常,检查地址:{} 错误码:{}", url, e.getErrorCode());
    throw new ServiceException("系统正忙,请稍后再试");
}

看出门道没???三层处理法??:

  1. 记录原始异常(留底)
  2. 转换业务异常(用户友好)
  3. 明确处理方案(降级/重试)

第二回合:异常封装的艺术

你可能会问:为啥要自己定义异常?直接抛原生的不香吗?举个栗子:

原生异常

java复制
throw new SQLException("Connection failed");

用户看到的效果:一堆技术术语,满脸问号

封装后的业务异常

java复制
throw new OrderException("订单创建失败,请联系客服", ErrorCode.ORDER_CREATE_ERROR);

??关键技巧??:

  • 继承RuntimeException避免checked异常污染
  • 携带业务错误码(方便前端处理)
  • 保留原始异常链(cause参数)

自定义异常模板:

java复制
public class BizException extends RuntimeException {
    private final int code;  // 错误码
    
    public BizException(String msg, int code) {
        super(msg);
        this.code = code;
    }
    
    // 带原始异常的构造方法
    public BizException(String msg, int code, Throwable cause) {
        super(msg, cause);
        this.code = code;
    }
}

第三回合:try-with-resources神操作

你知道Java7开始有个语法糖吗?能自动关流的写法,比传统方式省心十倍!

传统写法(容易忘关流):

java复制
FileInputStream fis = null;
try {
    fis = new FileInputStream("data.txt");
    // 处理文件...
} catch (IOException e) {
    // 处理异常
} finally {
    if (fis != null) {
        try { fis.close(); } catch (IOException e) { /* 忽略?危险! */ }
    }
}

优雅写法

java复制
try (FileInputStream fis = new FileInputStream("data.txt");
     ZipInputStream zis = new ZipInputStream(fis)) {
    // 自动关流!就算发生异常也会执行close()
} catch (IOException e) {
    throw new FileProcessException("文件处理失败", e);
}

??优势对比表??:

特性传统方式try-with-resources
自动关流???
代码简洁度冗长简洁
资源泄漏风险趋近于零

第四回合:异常处理金字塔法则

干了这么多年开发,我总结出个??三层处理原则??:

  1. ??底层??(DAO层):抓原始异常,转抛自定义异常
  2. ??中间层??(Service层):补充业务上下文信息
  3. ??顶层??(Controller层):最终捕获并返回统一格式

典型处理流程:

java复制
// Controller层
try {
    orderService.createOrder(params);
} catch (BizException e) {
    return Result.fail(e.getCode(), e.getMessage());
}

// Service层
public void createOrder() {
    try {
        orderDao.save();
    } catch (SQLException e) {
        throw new OrderException("订单保存失败", 5001, e); // 携带原始异常
    }
}

个人踩坑实录

曾经在支付模块偷懒没处理网络超时异常,结果用户重复付款...(此处应有心碎声)现在学乖了:

  1. ??重要操作必加事务回滚??(@Transactional注解)
  2. ??第三方调用必须设超时??(比如HttpClient设置timeout)
  3. ??异步操作要留补偿机制??(比如订单状态校验)

举个实战中的优雅处理案例:

java复制
try {
    paymentService.pay(orderId);
} catch (PaymentTimeoutException e) {
    // 启动补偿流程
    retryExecutor.schedule(() -> checkPaymentStatus(orderId), 5, TimeUnit.SECONDS);
    throw new BizException("支付正在处理中", 1001);
}

说到底,异常处理就像给代码穿西装——既要实用耐穿,又要体面好看。但别走极端啊!见过有人把每个方法都包三层try-catch,那代码读起来跟俄罗斯套娃似的。记住??黄金法则:该出手时才出手??,保持代码清爽才是真优雅!

本文由嘻道妙招独家原创,未经允许,严禁转载