Java异常处理的误区和经验总结

}

public RuntimeException(Integer errorCode, String message, Throwable cause) {

super(message, cause);

this.errorCode = errorCode;

}

public Integer getErrorCode() {

return errorCode;

}

}

正如示例代码所示,在异常中引入错误代码,一旦出现异常,我们只要将异常的错误代码呈现给用户,或者将错误代码转换成更通俗易懂的提示。其实这里的错误代码还包含另外一个功能,开发人员亦可以根据错误代码准确的知道了发生了什么类型异常。

误区三、对代码层次结构的污染

我们经常将代码分 Service、Business Logic、DAO 等不同的层次结构,DAO 层中会包含抛出异常的方法,如清单 2 所示:

清单 2

public Customer retrieveCustomerById(Long id) throw SQLException {

//根据 ID 查询数据库

}

上面这段代码咋一看没什么问题,但是从设计耦合角度仔细考虑一下,这里的 SQLException 污染到了上层调用代码,调用层需要显式的利用 try-catch 捕捉,或者向更上层次进一步抛出。根据设计隔离原则,我们可以适当修改成:

清单 3

public Customer retrieveCustomerById(Long id) {

try{

//根据 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);

最后

自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数Java工程师,想要提升技能,往往是自己摸索成长,自己不成体系的自学效果低效漫长且无助。

因此收集整理了一份《2024年Java开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Java开发知识点,不论你是刚入门Java开发的新手,还是希望在技术上不断提升的资深开发者,这些资料都将为你打开新的学习之门!

如果你觉得这些内容对你有帮助,需要这份全套学习资料的朋友可以戳我获取!!

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!
mg-IOC9DeIm-1715739989289)]

[外链图片转存中…(img-lOT0R4Aq-1715739989290)]

[外链图片转存中…(img-B6kSu2w0-1715739989290)]

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Java开发知识点,不论你是刚入门Java开发的新手,还是希望在技术上不断提升的资深开发者,这些资料都将为你打开新的学习之门!

如果你觉得这些内容对你有帮助,需要这份全套学习资料的朋友可以戳我获取!!

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值