打开APP
userphoto
未登录

开通VIP,畅享免费电子书等14项超值服

开通VIP
Java异常处理总结

题记:本文为工作十年回顾总结系列之Java语言之异常处理篇,主要内容为《Thinking in Java 》第四版和《Effective Java》第二版的阅读笔记,网上流传的最佳实践,以及个人工作总结的杂糅汇总,作为个人知识库存档备份,文中引用不再一一说明。

Java异常处理机制的目的,在于通过使用少于目前数量的代码来简化大型项目代码,提高项目的可靠性安全性,属于那种投入少但效果立竿见影的语言特性。

几个基本概念

1.传统(C时代)错误处理机制大都是约定俗成的,不是语言特性,比如通过很多的if语句来判断可预见错误,但如果每次方法调用都去做彻底的错误检查,程序会变得很臃肿繁杂。解决办法是用强制规定的形式来消除错误处理过程中随心所欲的因素,Java中异常处理机制就是如此;

2.“异常”这个词很形象,就如工作中出现了你没有预期到的问题,自己又无权处理,就只能找上级领导去搞定。Java异常处理机制的好处是降低错误处理的复杂度,在异常处理程序中,把“正常执行流程”和“出了问题怎么办的流程”代码区分,对代码的编写、阅读和调试都大有好处;

2.区分“异常情形”和“普通问题”,普通问题是当前环境下可以解决处理的错误,而“异常情形”则是没有足够信息来处理,只能抛给上一级环境。考虑除数为0的情形,究竟是普通问题还是异常情况?

Java异常基本语法

1.异常创建 和普通Java对象一样,用new在堆上创建,垃圾自动回收,对象被创建之后引用会交给throw。异常对象及其类型通常与方法返回参数不同,但从效果上看就是从方法返回的,可以简单把异常处理理解成一种不同的返回机制,两者有什么本质区别可不必深究。

2.捕获异常 ①监控区域(guarded region)是一段可能产生异常的代码块(try块),后面跟着异常处理代码(catch块),catch块可以有多个,异常处理机制根据参数与异常类型进行顺序匹配,使用基类Exception可以捕获所有异常,应该将其放在处理列表的末尾;②栈轨迹 可利用printStackTrace打印调用序列用来分析问题;③重新抛出异常 可以把捕获到的异常重新抛出,也可以调用fillInStackTrace方法更新抛出点信息;④异常链 Throwable子类在构造器中可以接受一个cause作为参数,通过把原始异常传递给新的异常,即使在当前位置创建并抛出了新的异常,也能通过这个异常链追踪到异常发生的最初位置。只有Error,Exception和RuntimeException提供了带cause的构造期,其他类型的则要用initCause方法。⑤使用finally进行清理 把除了内存之外资源恢复到它们的初始状态时,这类操作通常包括:已打开的文件或网络等;

3.自定义异常 Java自带的异常类型不可能包括所遇到的全部问题,可以自己定义异常类。自定义的异常类必须从已有的异常类进行继承。异常是个完全意义的类,可为其定义任意方法,因受检异常往往指明了可恢复的条件,所以提供一些辅助方法尤为重要,调用者借此可获得一些有助于恢复的信息;

4.异常限制 ①当覆盖方法时,只能抛出基类方法的异常说明里列出的那些异常,也可以不抛出任何异常;②异常限制对构造器不起作用,可以抛出声明之外的异常,并且派生类构造器不能捕获基类构造器抛出的异常;③异常说明本身并不属于方法类型的一部分,不能基于异常说明来重载方法;④对于在构造阶段可能会抛出异常,并且要求清理的类,最安全的使用方式是使用嵌套的Try子句;⑤抛出异常的时候,异常处理系统会按照代码书写顺序找出最近的处理程序,匹配的时候并不要求抛出的异常同处理程序所声明的异常完全匹配,派生类的对象也可以匹配其基类的处理程序。

Java标准异常类

Error错误:JVM内部的严重问题,无法恢复;

Exception异常:普通异常,通过合理的处理,程序还可以回到正常执行流程,包括受检异常和非受检异常。非受检异常也叫运行时异常(RuntimeException),这类异常是编程人员的逻辑问题,Java编译器不进行强制要求处理。可以这样理解两者区别:受检异常提出的是“法律下的自由”,必须遵守异常的约定才能自由编写代码,非受检异常则是“协约性质的自由”,你必须告诉我你要抛出什么异常,否则不会处理。所谓不处理就是异常会直达最上层,甚至被终端用户看到。

《EffectiveJava》对于异常使用的建议

第57条:异常只用于异常情况之下 异常永远不应该用于正常的控制流;

第58条:对可恢复的情况使用受检异常,对编程错误使用运行时异常 如果期望调用者能够适当恢复,对于这种情况就应该使用受检异常。通过抛出受检异常,强迫调用者在一个catch子句中处理该异常,或者将它传播出去。

第59条:避免不必要地使用受检的异常 受检异常是很好的特性,但过犹不及,调用者要么处理异常要么继续抛出,增加了调用者负担。

第60条:优先使用标准的异常 代码重用很重要,对异常也不例外。Java平台类库提供了一组基本的未受检异常,它们满足了绝大多数异常抛出需要,所以尽量少用自定义的异常。

第61条:抛出与抽象相对应的异常 高层实现应该捕获底层异常,同时抛出可以按高层抽象进行解释的异常,这种做法称为异常转译(exception translation),其中一种特殊的异常转译形式是异常链(exception chaining)。异常转译也不应该被滥用。

第62条:每个方法抛出的异常都要有文档 利用Javadoc描述方法所抛出异常重要,要始终单独声明受检异常,并且利用Javadoc的@throws标记,准确记录抛出每个异常的条件。同时,为非受检异常建立文档也非常明智的。

第63条:在细节消息中包含能捕获失败的信息 异常类型的toString方法应该尽可能多地返回有关失败原因的信息,细节信息应该包含有“对该异常有贡献的”参数和域的值。例如,IndexOutOfBoundsException异常的细节应该包含下届上届以及没有落在届内的下标志。

第64条:努力使失败保持原子性 一般而言,失败的方法调用应该使对象保持在被调用之前的状态,具有这种属性的方法被成为具有失败原子性(failure atomic)。大体有四种方法可以实现这种效果,具体查阅原书。

第65条:不要忽略异常 本条建议适用于受检异常和未受检异常,是一条显而易见但是常被违反的建议,空的catch块会使异常达不到应有的目的,就算真的不处理,也要包含一条说明,解释忽略原因!


阿里巴巴Java开发规范异常部分

最近看到一份阿里巴巴的开发规范,总结的很详细,非常有参考价值,这里拿来和上文进行对照。

1.【强制】Java 类库中定义的一类RuntimeException可以通过预先检查进行规避,而不应该通过catch 来处理,比如:IndexOutOfBoundsException,NullPointerException等等;

2.【强制】异常不要用来做流程控制,条件控制,因为异常的处理效率比条件分支低;

3.【强制】对大段代码进行try-catch是不负责任的表现。catch时请分清稳定代码和非稳定代码,稳定代码指的是无论如何不会出错的代码,对于非稳定代码的catch尽可能进行区分异常类型,再做对应的异常处理

4.【强制】捕获异常是为了处理它,不要捕获了却什么都不处理而抛弃之,如果不想处理它,请将该异常抛给它的调用者。最外层的业务使用者,必须处理异常,将其转化为用户可以理解的内容;

5.【强制】有try块放到了事务代码中,catch异常后,如果需要回滚事务,一定要注意手动回滚事务;

6.【强制】finally块必须对资源对象、流对象进行关闭,有异常也要做try-catch。如果JDK7以上,可以使用try-with-resources方式;

7.【强制】不能在finally块中使用return,finally块中的return返回后方法结束执行,不会再执行try块中的return语句;

8.【强制】捕获异常与抛异常,必须是完全匹配,或者捕获异常是抛异常的父类。 说明:如果预期对方抛的是绣球,实际接到的是铅球,就会产生意外情况;

9.【推荐】方法的返回值可以为null,不强制返回空集合,或者空对象等,必须添加注释充分说明什么情况下会返回null值。调用方需要进行null判断防止NPE问题;

10.【推荐】防止NPE是程序员的基本修养。NPE产生的场景:①返回类型为包装数据类型,有可能是null,返回int值时注意判空;②数据库的查询结果可能为null;③集合里的元素即使isNotEmpty,取出的数据元素也可能为null;④远程调用返回对象,一律要求进行NPE判断;⑤对于Session中获取的数据,建议NPE检查避免空指针;⑥级联调用obj.getA.getB.getC易产生NPE;

11.【推荐】在代码中使用“抛异常”还是“返回错误码”,对于公司外的http/api开放接口必须使用“错误码”,而应用内部推荐异常抛出;跨应用间RPC调用优先考虑使用Result方式,封装isSuccess、“错误码”、“错误简短信息”;

12.【推荐】定义时区分unchecked / checked 异常,避免直接使用RuntimeException抛出,更不允许抛出Exception或者Throwable,应使用有业务含义的自定义异常。推荐业界已定义过的自定义异常,如DAOException / ServiceException等;

13.【参考】避免出现重复的代码(Don’t Repeat Yourself),即DRY原则。 说明:随意复制和粘贴代码,必然会导致代码的重复,在以后需要修改时,需要修改所有的副本,容易遗漏。必要时抽取共性方法,或者抽象公共类,甚至是共用模块。

实践总结在TIJ中Bruce Eckel最后总结:尽管异常通常被认为是一种工具,使得你可以在执行过程中报告错误,并从错误中恢复,但是所谓的恢复聊胜于无,不过报告功能室异常的精髓所在,Java坚定地强调所有错误都以异常形式报告这一事实,正是它远远超过异常的精髓所在。对于这一点,在实际项目中可谓是深有体会,既然程序会出问题,主要是开发人员开发忽略了一些关键点,如果没有正确合理的使用异常,并且留下相应的日志,在解决问题的时候就不能发现错误根源,也就不能快速解决问题,生产环境尤为重要!

本站仅提供存储服务,所有内容均由用户发布,如发现有害或侵权内容,请点击举报
打开APP,阅读全文并永久保存 查看更多类似文章
猜你喜欢
类似文章
【热】打开小程序,算一算2024你的财运
Java之异常处理
全面了解Java异常捕获方法
分享Java处理异常的几个小知识
J2EE项目异常处理 - dongming - JavaEye技术网站
异常
有关JAVA异常和错误(ERROR)的处理
更多类似文章 >>
生活服务
热点新闻
分享 收藏 导长图 关注 下载文章
绑定账号成功
后续可登录账号畅享VIP特权!
如果VIP功能使用有故障,
可点击这里联系客服!

联系客服