Java异常的捕获和处理

捕获和处理异常

  • 在业务代码层面考虑异常处理,框架层面的粗犷捕获仅做兜底

对于自定义的业务异常, 以warn级别的日志记录当前URL、执行方法等信息后,提取异常中的错误码和消息到合适的API包装体返回给API调用方。

对于无法处理的系统异常,以Error级别的日志记录上下文信息(URL、参数、用户ID)后,转换为“服务器忙,请稍后再试”,同样以API包装体返回给调用方。

  • 不准生吞异常

捕获异常后可以不抛出,但至少要记录日志

  • 不准丢弃异常的原始信息

可以使用如下方法来保留异常的原始信息

catch (IOException e) {
    log.error("文件读取错误", e);
    throw new RuntimeException("系统忙请稍后再试");
}

或者把原始异常作为转换后新异常的cause,原始异常信息同样不会丢

catch (IOException e) {
    throw new RuntimeException("系统忙请稍后再试", e);
}
  • 抛出异常时要指定具体信息

下面的代码中未在()中填写具体的message,导致日志记录为java.lang.RuntimeException: null,容易误解为空指针异常,实际上是因为异常的message为空

throw new RuntimeException();
  • 小心finally中的异常

下面代码中try中的逻辑出现异常,却会被finally中的异常覆盖。为什么异常会被覆盖?因为一个方法无法出现两个异常。

@GetMapping("wrong")
public void wrong() {
    try {
        log.info("try");
        //异常丢失
        throw new RuntimeException("try");
    } finally {
        log.info("finally");
        throw new RuntimeException("finally");
    }
}

解决方法:finally代码块自己负责异常捕获和处理

@GetMapping("right")
public void right() {
    try {
        log.info("try");
        throw new RuntimeException("try");
    } finally {
        log.info("finally");
        try {
            throw new RuntimeException("finally");
        } catch (Exception ex) {
            log.error("finally", ex);
        }
    }
}

或者可以把try中的异常作为主异常抛出,使用addSuppressed方法把finally中的异常附加到主异常上

@GetMapping("right2")
public void right2() throw Exception{
  Exception e = null;
  try{
    log.info("try");
    throw new RunTimeException("try");
  } catch (Exception ex){
    e = ex;
  } finally {
    log.info("finally");
    try {
      log.info("finally");
      try{
        throw new RunTimeException("finally");
      } catch (Exception ex){
        if (e != null){
          e.addSuppressed(ex);
        }
        else {
          e = ex;
        }
      }
    }
  }
 throw e;
}

运行方法可以得到同时包含主异常和被屏蔽的异常。这个就是try-with-resource语句的方法,对于实现了AutoCloseable接口的资源,建议使用try-with-resource来释放资源,否则在释放资源时出现的异常覆盖主异常的问题。

传统try-finally语句,在try中调用read方法,finally中调用close方法,会造成finally异常覆盖try中的异常

使用try-with-resource模式可以避免上述问题:

@GetMapping("useresourceright")
public void useresourceright() throws Exception {
    try (TestResource testResource = new TestResource()){
        testResource.read();
    } catch (IOException e) {
        throw new RuntimeException(e.getMessage(), e);
    }
}

将外部资源的句柄对象的创建放在try后面的括号中,当这个try-catch代码块执行完毕后,Java会确保外部资源的close方法被调用。try-with-resource只是JDK实现的一个语法糖,当上面代码编译后,会成为之前那种复杂的写法。反编译后的代码中有一个addSuppressed方法,叫做异常抑制。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值