Java异常处理的误区和经验总结,一线互联网架构师筑基必备技能之Java篇

本文探讨了在Java编程中常见的异常处理错误,包括错误地将异常堆栈输出到日志、在循环中包含异常处理、使用Exception捕获所有异常、多层次封装非检测异常、重复打印异常信息、异常信息不足以及无法预知潜在异常等问题。并提出了重构建议,如精确捕获异常、提供足够的异常信息以及避免异常信息冗余等最佳实践。
摘要由CSDN通过智能技术生成
   * 而在 Production 环境中,需要将错误堆栈输出到日志。

   * 而且这里 catch 处理之后程序继续执行,会导致进一步的问题*/



      ex.printStacktrace();

 }

}




可以重构成:



清单 5



public void retrieveObjectById(Long id){

try{

//..some code that throws SQLException

}

catch(SQLException ex){

throw new RuntimeException(“Exception in retieveObjectById”, ex);

}

finally{

//clean up resultset, statement, connection etc

}

}




这个误区比较基本,一般情况下都不会犯此低级错误。



### 误区五、将异常包含在循环语句块中



如下代码所示,异常包含在 for 循环语句块中。



清单 6



for(int i=0; i<100; i++){

try{

}catch(XXXException e){

     //….

}

}




我们都知道异常处理占用系统资源。一看,大家都认为不会犯这样的错误。换个角度,类 A 中执行了一段循环,循环中调用了 B 类的方法,B 类中被调用的方法却又包含 try-catch 这样的语句块。褪去类的层次结构,代码和上面如出一辙。



### 误区六、利用 Exception 捕捉所有潜在的异常



一段方法执行过程中抛出了几个不同类型的异常,为了代码简洁,利用基类 Exception 捕捉所有潜在的异常,如下例所示:



清单 7



public void retrieveObjectById(Long id){

try{

    //…抛出 IOException 的代码调用

    //…抛出 SQLException 的代码调用

}catch(Exception e){

    //这里利用基类 Exception 捕捉的所有潜在的异常,如果多个层次这样捕捉,会丢失原始异常的有效信息

    throw new RuntimeException(“Exception in retieveObjectById”, e);

}

}




可以重构成



清单 8



public void retrieveObjectById(Long id){

try{

    //..some code that throws RuntimeException, IOException, SQLException

}catch(IOException e){

    //仅仅捕捉 IOException

    throw new RuntimeException(/*指定这里 IOException 对应的错误代码*/code,“Exception in retieveObjectById”, e);

}catch(SQLException e){

    //仅仅捕捉 SQLException

    throw new RuntimeException(/*指定这里 SQLException 对应的错误代码*/code,“Exception in retieveObjectById”, e);

}

}




### 误区七、多层次封装抛出非检测异常



如果我们一直坚持不同类型的异常一定用不同的捕捉语句,那大部分例子可以绕过这一节了。但是如果仅仅一段代码调用会抛出一种以上的异常时,很多时候没有必要每个不同类型的 Exception 写一段 catch 语句,对于开发来说,任何一种异常都足够说明了程序的具体问题。



清单 9



try{

//可能抛出 RuntimeException、IOExeption 或者其它;

//注意这里和误区六的区别,这里是一段代码抛出多种异常。以上是多段代码,各自抛出不同的异常

}catch(Exception e){

//一如既往的将 Exception 转换成 RuntimeException,但是这里的 e 其实是 RuntimeException 的实例,已经在前段代码中封装过

throw new RuntimeException(/**/code, /**/, e);

}




如果我们如上例所示,将所有的 Exception 再转换成 RuntimeException,那么当 Exception 的类型已经是 RuntimeException 时,我们又做了一次封装。将 RuntimeException 又重新封装了一次,进而丢失了原有的 RuntimeException 携带的有效信息。



解决办法是我们可以在 RuntimeException 类中添加相关的检查,确认参数 Throwable 不是 RuntimeException 的实例。如果是,将拷贝相应的属性到新建的实例上。或者用不同的 catch 语句块捕捉 RuntimeException 和其它的 Exception。个人偏好方式一,好处不言而喻。



### 误区八、多层次打印异常



我们先看一下下面的例子,定义了 2 个类 A 和 B。其中 A 类中调用了 B 类的代码,并且 A 类和 B 类中都捕捉打印了异常。



清单 10



private static Logger logger = LoggerFactory.getLogger(A.class);

public void process(){

 try{

 //实例化 B 类,可以换成其它注入等方式

 B b = new B();

 b.process();

 //other code might cause exception

} catch(XXXException e){

   //如果 B 类 process 方法抛出异常,异常会在 B 类中被打印,在这里也会被打印,从而会打印 2 次

   logger.error(e);

   throw new RuntimeException(/* 错误代码 */ errorCode, /*异常信息*/msg, e);

   }

}

}

public class B{

private static Logger logger = LoggerFactory.getLogger(B.class);

public void process(){

    try{

        //可能抛出异常的代码

    }

    catch(XXXException e){

        logger.error(e);

        throw new RuntimeException(/* 错误代码 */ errorCode, /*异常信息*/msg, e);

    }

}

}




同一段异常会被打印 2 次。如果层次再复杂一点,不去考虑打印日志消耗的系统性能,仅仅在异常日志中去定位异常具体的问题已经够头疼的了。



其实打印日志只需要在代码的最外层捕捉打印就可以了,异常打印也可以写成 AOP,织入到框架的最外层。



### 误区九、异常包含的信息不能充分定位问题



异常不仅要能够让开发人员知道哪里出了问题,更多时候开发人员还需要知道是什么原因导致的问题,我们知道 java .lang.Exception 有字符串类型参数的构造方法,这个字符串可以自定义成通俗易懂的提示信息。



简单的自定义信息开发人员只能知道哪里出现了异常,但是很多的情况下,开发人员更需要知道是什么参数导致了这样的异常。这个时候我们就需要将方法调用的参数信息追加到自定义信息中。下例只列举了一个参数的情况,多个参数的情况下,可以单独写一个工具类组织这样的字符串。



清单 11



public void retieveObjectById(Long id){

try{

    //..some code that throws SQLException

}catch(SQLException ex){

    //将参数信息添加到异常信息中

    throw new RuntimeException(“Exception in retieveObjectById with Object Id :”+ id, ex);

}

}




### 误区十、不能预知潜在的异常




# 面试准备+复习资料分享:

> 为了应付面试也刷了很多的面试题与资料,现在就分享给有需要的读者朋友,资料我只截取出来一部分哦,有需要的可以来找我获取哈

**[获取方式:点击蓝色字体即可免费获取](https://gitee.com/vip204888/java-p7)**

![秋招|美团java一面二面HR面面经,分享攒攒人品](https://img-blog.csdnimg.cn/img_convert/54384e991f0f261fe820c6ffaa86fd69.png)

  throw new RuntimeException(“Exception in retieveObjectById with Object Id :”+ id, ex);

   }

} 

误区十、不能预知潜在的异常

面试准备+复习资料分享:

为了应付面试也刷了很多的面试题与资料,现在就分享给有需要的读者朋友,资料我只截取出来一部分哦,有需要的可以来找我获取哈

获取方式:点击蓝色字体即可免费获取

[外链图片转存中…(img-ByED5MJx-1628576184497)]

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值