10年Java开发经验,Java异常处理的误区和经验总结(1)

        //根据 ID 查询数据库

 }catch(SQLException e){

        //利用非检测异常封装检测异常,降低层次耦合

        throw new RuntimeException(SQLErrorCode, e);

 }finally{

        //关闭连接,清理资源

 }

}




### 误区四、忽略异常



如下异常处理只是将异常输出到控制台,没有任何意义。而且这里出现了异常并没有中断程序,进而调用代码继续执行,导致更多的异常。



清单 4



public void retrieveObjectById(Long id){

try{

   //..some code that throws SQLException

}catch(SQLException ex){

 /**

   *了解的人都知道,这里的异常打印毫无意义,仅仅是将错误堆栈输出到控制台。

   * 而在 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 次。如果层次再复杂一点,不去考虑打印日志消耗的系统性能,仅仅在异常日志中去定位异常具体的问题已经够头疼的了。


# 文末java面试题,进阶技术大纲,架构资料分享

我将这三次阿里面试的题目全部分专题整理出来,并附带上详细的答案解析,生成了一份**PDF文档**,有兴趣的朋友们[可以点击这里即可免费领取](https://gitee.com/vip204888/java-p7)

*   **第一个要分享给大家的就是算法和数据结构**

![网易严选Java开发三面面经:HashMap+JVM+索引+消息队列](https://img-blog.csdnimg.cn/img_convert/9085dbf8d9d275eab39204ef48a2562a.png)

*   **第二个就是数据库的高频知识点与性能优化**

![网易严选Java开发三面面经:HashMap+JVM+索引+消息队列](https://img-blog.csdnimg.cn/img_convert/23104399982cb3c920db299e13670822.png)

*   **第三个则是并发编程(72个知识点学习)**

![网易严选Java开发三面面经:HashMap+JVM+索引+消息队列](https://img-blog.csdnimg.cn/img_convert/a31ea1be7fdbd3b8ffdb3f1de0efc0d7.png)

*   **最后一个是各大JAVA架构专题的面试点+解析+我的一些学习的书籍资料**

![网易严选Java开发三面面经:HashMap+JVM+索引+消息队列](https://img-blog.csdnimg.cn/img_convert/b8aba59aff00f39f817182f3e1ea3555.png)

e.com/vip204888/java-p7)

*   **第一个要分享给大家的就是算法和数据结构**

[外链图片转存中...(img-z4md5bZf-1628063972543)]

*   **第二个就是数据库的高频知识点与性能优化**

[外链图片转存中...(img-niOlJ4CU-1628063972546)]

*   **第三个则是并发编程(72个知识点学习)**

[外链图片转存中...(img-4jw1rY0Q-1628063972548)]

*   **最后一个是各大JAVA架构专题的面试点+解析+我的一些学习的书籍资料**

[外链图片转存中...(img-J0pzSkid-1628063972550)]

还有更多的Redis、MySQL、JVM、Kafka、微服务、Spring全家桶等学习笔记这里就不一一列举出来
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值