Java:处理 Exception 的几种实践,很优雅,值得推荐!
阅读本文大概需要 5 分钟。
来自:http://ww7.rowkey.me/
在Java中处理异常并不是一个简单的事情。不仅仅初学者很难理解,即使一些有经验的开发者也需要花费很多时间来思考如何处理异常,包括需要处理哪些异常,怎样处理等等。
这也是绝大多数开发团队都会制定一些规则来规范对异常的处理的原因。而团队之间的这些规范往往是截然不同的。
本文给出几个被很多团队使用的异常处理最佳实践。
在Finally块中清理资源或者使用try-with-resource语句
当使用类似InputStream这种需要使用后关闭的资源时,一个常见的错误就是在try块的最后关闭资源。
public void doNotCloseResourceInTry() { FileInputStream inputStream = null; try { File file = new File("./tmp.txt"); inputStream = new FileInputStream(file); // use the inputStream to read a file // do NOT do this inputStream.close(); } catch (FileNotFoundException e) { log.error(e); } catch (IOException e) { log.error(e); }}
上述代码在没有任何exception的时候运行是没有问题的。但是当try块中的语句抛出异常或者自己实现的代码抛出异常,那么就不会执行最后的关闭语句,从而资源也无法释放。
合理的做法则是将所有清理的代码都放到finally块中或者使用try-with-resource语句。
public void closeResourceInFinally() { FileInputStream inputStream = null; try { File file = new File("./tmp.txt"); inputStream = new FileInputStream(file); // use the inputStream to read a file } catch (FileNotFoundException e) { log.error(e); } finally { if (inputStream != null) { try { inputStream.close(); } catch (IOException e) { log.error(e); } } }}
public void automaticallyCloseResource() { File file = new File("./tmp.txt"); try (FileInputStream inputStream = new FileInputStream(file);) { // use the inputStream to read a file } catch (FileNotFoundException e) { log.error(e); } catch (IOException e) { log.error(e); }}
指定具体的异常
尽可能的使用最具体的异常来声明方法,这样才能使得代码更容易理解。
public void doNotDoThis() throws Exception { ...}public void doThis() throws NumberFormatException { ...}
如上,NumberFormatException字面上即可以看出是数字格式化错误。
对异常进行文档说明
当在方法上声明抛出异常时,也需要进行文档说明。和前面的一点一样,都是为了给调用者提供尽可能多的信息,从而可以更好地避免/处理异常。异常处理的 10 个最佳实践,这篇也推荐看下。
在Javadoc中加入throws声明,并且描述抛出异常的场景。
/** * This method does something extremely useful ... * * @param input * @throws MyBusinessException if ... happens */public void doSomething(String input) throws MyBusinessException { ...}
抛出异常的时候包含描述信息
在抛出异常时,需要尽可能精确地描述问题和相关信息,这样无论是打印到日志中还是监控工具中,都能够更容易被人阅读,从而可以更好地定位具体错误信息、错误的严重程度等。
但这里并不是说要对错误信息长篇大论,因为本来Exception的类名就能够反映错误的原因,因此只需要用一到两句话描述即可。
try { new Long("xyz");} catch (NumberFormatException e) { log.error(e);}
NumberFormatException即告诉了这个异常是格式化错误,异常的额外信息只需要提供这个错误字符串即可。当异常的名称不够明显的时候,则需要提供尽可能具体的错误信息。
首先捕获最具体的异常
现在很多IDE都能智能提示这个最佳实践,当你试图首先捕获最笼统的异常时,会提示不能达到的代码。当有多个catch块中,按照捕获顺序只有第一个匹配到的catch块才能执行。因此,如果先捕获IllegalArgumentException,那么则无法运行到对NumberFormatException的捕获。
public void catchMostSpecificExceptionFirst() { try { doSomething("A message"); } catch (NumberFormatException e) { log.error(e); } catch (IllegalArgumentException e) { log.error(e) }}
# 不要捕获Throwable
Throwable是所有异常和错误的父类。你可以在catch语句中捕获,但是永远不要这么做。如果catch了throwable,那么不仅仅会捕获所有exception,还会捕获error。而error是表明无法恢复的jvm错误。因此除非绝对肯定能够处理或者被要求处理error,不要捕获throwable。
public void doNotCatchThrowable() { try { // do something } catch (Throwable t) { // don't do this! }}
不要忽略异常
很多时候,开发者很有自信不会抛出异常,因此写了一个catch块,但是没有做任何处理或者记录日志。
public void doNotIgnoreExceptions() { try { // do something } catch (NumberFormatException e) { // this will never happen }}
但现实是经常会出现无法预料的异常或者无法确定这里的代码未来是不是会改动(删除了阻止异常抛出的代码),而此时由于异常被捕获,使得无法拿到足够的错误信息来定位问题。合理的做法是至少要记录异常的信息。
public void logAnException() { try { // do something } catch (NumberFormatException e) { log.error("This should never happen: " + e); }}
不要记录并抛出异常
可以发现很多代码甚至类库中都会有捕获异常、记录日志并再次抛出的逻辑。如下:
try { new Long("xyz");} catch (NumberFormatException e) { log.error(e); throw e;}
这个处理逻辑看着是合理的。但这经常会给同一个异常输出多条日志。如下:
17:44:28,945 ERROR TestExceptionHandling:65 - java.lang.NumberFormatException: For input string: "xyz"Exception in thread "main" java.lang.NumberFormatException: For input string: "xyz"at java.lang.NumberFormatException.forInputString(NumberFormatException.java:65)at java.lang.Long.parseLong(Long.java:589)at java.lang.Long.(Long.java:965)at com.stackify.example.TestExceptionHandling.logAndThrowException(TestExceptionHandling.java:63)at com.stackify.example.TestExceptionHandling.main(TestExceptionHandling.java:58)
如上所示,后面的日志也没有附加更有用的信息。如果想要提供更加有用的信息,
那么可以将异常包装为自定义异常。
public void wrapException(String input) throws MyBusinessException { try { // do something } catch (NumberFormatException e) { throw new MyBusinessException("A message that describes the error.", e); }}
因此,仅仅当想要处理异常时才去捕获,否则只需要在方法签名中声明让调用者去处理
包装异常时不要抛弃原始的异常
捕获标准异常并包装为自定义异常是一个很常见的做法。这样可以添加更为具体的异常信息并能够做针对的异常处理。
需要注意的是,包装异常时,一定要把原始的异常设置为cause(Exception有构造方法可以传入cause)。否则,丢失了原始的异常信息会让错误的分析变得困难。
public void wrapException(String input) throws MyBusinessException { try { // do something } catch (NumberFormatException e) { throw new MyBusinessException("A message that describes the error.", e); }}
总结
综上可知,当抛出或者捕获异常时,有很多不一样的东西需要考虑。其中的许多点都是为了提升代码的可阅读性或者api的可用性。
异常不仅仅是一个错误控制机制,也是一个沟通媒介,因此与你的协作者讨论这些最佳实践并制定一些规范能够让每个人都理解相关的通用概念并且能够按照同样的方式使用它们。
IT技术分享社区
个人博客网站:https://programmerblog.xyz
文章推荐程序员效率:画流程图常用的工具程序员效率:整理常用的在线笔记软件远程办公:常用的远程协助软件,你都知道吗?51单片机程序下载、ISP及串口基础知识硬件:断路器、接触器、继电器基础知识
Java:处理 Exception 的几种实践,很优雅,值得推荐!相关推荐
- 处理 Exception 的几种实践,很优雅,被很多团队采纳!
点击关注公众号,Java干货及时送达 在Java中处理异常并不是一个简单的事情.不仅仅初学者很难理解,即使一些有经验的开发者也需要花费很多时间来思考如何处理异常,包括需要处理哪些异常,怎样处理等等. ...
- 处理 Exception 的几种实践,被很多团队采纳!
微信搜索逆锋起笔关注后回复编程pdf 领取编程大佬们所推荐的 23 种编程资料! 来源:http://ww7.rowkey.me/ 在 Java 中处理异常并不是一个简单的事情.不仅仅初学者很难理解, ...
- Java 异常处理的 9 个最佳实践
Java 异常处理的 9 个最佳实践 原文地址:https://dzone.com/articles/9-... 翻译出处:https://www.oschina.net/trans... 在 Jav ...
- 《转载》Java异常处理的10个最佳实践
本文转载自 ImportNew - 挖坑的张师傅 异常处理在编写健壮的 Java 应用中扮演着非常重要的角色.异常处理并不是功能性需求,它需要优雅地处理任何错误情况,比如资源不可用.非法的输入.nul ...
- 关于JAVA异常处理的20个最佳实践
关于JAVA异常处理的20个最佳实践 在我们深入了解异常处理最佳实践的深层概念之前,让我们从一个最重要的概念开始,那就是理解在JAVA中有三种一般类型的可抛类: 检查性异常(checked excep ...
- 小菜:Java异常处理的10个最佳实践
转载自 小菜:Java异常处理的10个最佳实践 异常处理在编写健壮的 Java 应用中扮演着非常重要的角色.异常处理并不是功能性需求,它需要优雅地处理任何错误情况,比如资源不可用.非法的输入.nu ...
- Java底层抛出异常_总结java的exception
这篇和前面的 总结java的interface和abstract class 一样.跳过最基础语法不聊,只说一些比较tricky的东西和一些好的practice. 语法: Exception继承自Th ...
- 【java】Java 动态调试技术原理及实践
1.概述 转载:Java 动态调试技术原理及实践 一.动态调试要解决的问题 断点调试是我们最常使用的调试手段,它可以获取到方法执行过程中的变量信息,并可以观察到方法的执行路径.但断点调试会在断点位置停 ...
- Java 创建线程的三种方法比较
在学习编程的过程中,我觉得不止要获得课本的知识,更多的是通过学习技术知识提高解决问题的能力,这样我们才能走在最前方,本文主要讲述Java 创建线程的三种方法比较, 更多Java专业知识,广州疯狂jav ...
最新文章
- mysql 存储过程月单拆天单_MySQL之存储过程按月创建表
- git 建立一个私有模块
- python【蓝桥杯vip练习题库】ALGO-189 P0505(阶乘问题)
- Android中Handler消息传递机制应用之子线程不允许操作主线程的组件
- 人脸识别撞脸名画_与名画“撞脸”火爆数博会 观众直呼“太好玩”【高清组图】...
- axure 素材_Axure原型:超漂亮的系统首页
- 二元偏导数存在的条件_多元函数 可导、可微、连续、一阶偏导数连续 之间关系的总结...
- mini6410 uboot board.c 分析
- Flume之介绍 核心组件 可靠性 恢复性
- python 绘制图表生成svg文件_使用Python创建SVG
- XAP部署错误代码大全
- host smbus controller not enabled解决方法(主机smbus控制器未启用)
- linux中ftp禁止匿名,linux下禁止root和匿名用户登录ftp
- JDBC 数据库增删改查的通用代码示例详解
- mysql 1449 问题
- matlab ode45修改,matlab ode45增加输入值
- linux ps 命令大全
- ElasticSearch-6.8.11实践笔记
- dockerexec 的使用-it操作
- vue.js创建网站实例1
热门文章
- Spring jdbc 对象Mapper的简单封装
- HALCON示例程序classify_image_class_mlp.hdev如何使用MLP分类器分割RGB图像
- ANSYS 简支梁的约束
- linux挂载VMFS硬盘,ESX4.1挂载NFS共享存储(VMkernel)
- 李倩星r语言实战_《基于R的统计分析与数据挖掘》教学大纲
- FCN-加载训练与测试数据
- rocketmq 消息 自定义_RocketMQ消息轨迹-设计篇
- javascript 在对象中使用 定时器_如何使用JavaScript 面向对象编程
- Linux抓eth0网卡包的命令,Linux系统使用tcpdump命令抓包
- angular接口传参