JAVA和Nginx 教程大全

网站首页 > 精选教程 正文

Java编程中,13条优雅处理异常的最佳实践

wys521 2024-11-20 22:51:55 精选教程 17 ℃ 0 评论

1. 不要使用异常来控制业务逻辑

在开发具体业务时,很多人会使用异常来控制业务逻辑,例如:

try{
execute();
}catch(Exception e){
execute1();
}catch(Exception1 e1){
execute2();
}

代码中的分支逻辑应该使用if-else来控制,而不是依赖异常。使用异常来控制代码逻辑不容易理解,难于维护。

2. 如果处理不了,请不要捕获

很多人在开发中,遇到try语句块,后面一定会跟一个catch块,这是不对的。我们在开发中,应该只捕获那些能处理的异常,如果处理不了,就不要捕获它,继续向上抛,谁能解决谁来捕获。

像下面这段代码对于异常的处理就毫无意义:

try{

}catch (Exception e){

throw e;

}

3. 在catch 语句中不要使用printStackTrace()

由于现在的开发工具IDE都比较智能,当我们在写一段try-catch代码时,通常会自动生成printStackTrace()语句,例如:

try{
execute();
}catch (Exception e){
e.printStackTrace();
}

需要注意的是,e.printStackTrace()并不是处理异常,很多人认为这是在打印异常信息,也是在处理异常。

但是,e.printStackTrace()表示把异常信息输出到控制台,而不是日志中。e.printStackTrace()只能在调试阶段使用,程序在线上运行时,错误的信息要通过日志输出。

4. 二次抛出异常时,要带上异常链

我们在处理异常时,有时可能会先捕获一个异常,再抛出另一个异常。这种场景一般用于抛出一些特定的、更容易理解的业务异常。

这样做是可以的,但在抛出新的异常时,一定要把被捕获的那个异常的异常信息也带上,避免丢失异常的堆栈。例如:

try{
}catch (Exception e){
// 错误用法
throw new MyException();
// 正确用法
throw new MyException(e);
}

5. 在需要的地方声明特定的受检异常

受检异常的最大特点是要求调用者必须明确地处理这个异常,这其实是一种强制性的约束。所以,当代码中有一些特殊情况需要让调用者必须关注时,要使用受检异常,起到提醒的作用。

6. 异常捕获的顺序需要特殊注意

很多人知道异常需要处理,并且尝试在代码中捕获异常,因为可能有很多异常抛出,所以会同时捕获多个异常,于是有人就写出了以下代码:

try{
}catch (Exception e){
}catch (MyException e){
}catch (IllegalArgumentException e){
}

以上处理方式最大的问题就是异常的捕获顺序不合理,以上形式的捕获异常,后面的MyException和IllegalArgumentException永远不会被捕获,异常一旦发生就会被Exception直接捕获了。

所以,在捕获异常时,要把范围较小的异常放到前面,对于RuntimeException、Exception和Throwable的捕获一定要放到最后。

7. 可以直接捕获Exception,但是要注意场景

在关于异常的处理上,很多人建议不要直接对Exception、Throwable进行捕获,因为捕获的范围太大了,会导致永远无法知道异常的具体细节。

其实有时我们可能还真的需要对Exception、甚至Throwable进行捕获,尤其是现在很多应用都微服务化了,经常会有各种RPC接口的互相调用。

我们在给外部提供一个RPC接口时,应该通过错误码的形式传递错误信息,而不是把异常抛给调用方。因为A系统的异常抛给B系统,B系统是一定处理不了的。

所以,我们往往需要在RPC接口中对Exception进行捕获,以避免异常交给外部系统。

8. 可以直接捕获Throwable,但是要注意场景

Throwable有Error和Exception两个子类,通常我们认为Error是程序员处理不了的,所以不建议捕获。

但是有一种特殊的情况,我们可能需要捕获Error。

当我们提供RPC服务时,一旦服务被调用过程中发生了Error,如NoSuchMethodError,我们没有捕获,那么这个错误就会一直往上抛,最终被RPC框架捕获。

RPC框架捕获这个错误之后,可能会把错误日志打印到它自己的日志文件中,而不是我们应用的业务日志中。

通常RPC框架自己的日志会有很多各种超时等异常,我们很少对其进行错误监控,这就可能导致错误发生了,但我们无法察觉。

9. 不要在finally 中抛出异常

示例代码如下:

try {
execute();
}finally
{
throw new Exception();
}

当execute方法抛出异常之后,我们在finally中再次抛出一个异常,这就导致execute方法抛出的那个异常信息完全丢失了。丢失了异常链,会给后期的问题排查带来很大的困难。

10. 在finally 中释放资源

当我们想要释放一些资源时,如数据库链接、文件链接等,需要在finally中进行释放,因为finally中的代码一定会执行。

11. 如果不想处理异常,则使用finally 块而不是catch 块

因为我们要在finally中释放资源,所以很多开发者会顺手把try-catch-finally都写上,这其实是错误的。

当我们不想处理一个异常,又想在异常发生后做一些事情的时候,不要写catch块,而是使用finally块。

12. 善于使用自定义异常

我们在日常开发中会接触很多异常,JDK内置了很多异常,一些框架中也定义了自己的异常。我们也可以自定义一些业务异常,这些异常可以有一定的继承关系,方便我们快速地识别异常的原因,以及快速恢复。比如OrderCanceledException、LoginFailedException等,我们通过这些异常的名字就知道具体发生了什么。

13. try 块中的代码要尽可能的少

不要在一个几百行代码外面加一个try进行异常处理,我们需要控制try的粒度,对于那些明显不会发生异常的代码,就不要把它们放到try块中。


内容摘自《深入理解Java核心技术》,作者是Hollis,张洪亮,阿里巴巴技术专家,51CTO 专栏作家,CSDN 博客专家,掘金优秀作者,《程序员的三门课》联合作者,《Java工程师成神之路》系列文章作者;热衷于分享计算机编程相关技术,博文全网阅读量数千万。

本文暂时没有评论,来添加一个吧(●'◡'●)

欢迎 发表评论:

最近发表
标签列表