题记:本文为工作十年回顾总结系列之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坚定地强调所有错误都以异常形式报告这一事实,正是它远远超过异常的精髓所在。对于这一点,在实际项目中可谓是深有体会,既然程序会出问题,主要是开发人员开发忽略了一些关键点,如果没有正确合理的使用异常,并且留下相应的日志,在解决问题的时候就不能发现错误根源,也就不能快速解决问题,生产环境尤为重要!

(THE END)

Java异常处理总结的更多相关文章

  1. 札记:Java异常处理

    异常概述 程序在运行中总会面临一些"意外"情况,良好的代码需要对它们进行预防和处理.大致来说,这些意外情况分三类: 交互输入 用户以非预期的方式使用程序,比如非法输入,不正当的操作 ...

  2. java异常处理(父子异常的处理)

    我当初学java异常处理的时候,对于父子异常的处理,我记得几句话“子类方法只能抛出父类方法所抛出的异常或者是其子异常,子类构造器必须要抛出父类构造器的异常或者其父异常”.那个时候还不知道子类方法为什么 ...

  3. Java 异常处理

    异常是程序中的一些错误,但并不是所有的错误都是异常,并且错误有时候是可以避免的. 比如说,你的代码少了一个分号,那么运行出来结果是提示是错误java.lang.Error:如果你用System.out ...

  4. 《转载》Java异常处理的10个最佳实践

    本文转载自 ImportNew - 挖坑的张师傅 异常处理在编写健壮的 Java 应用中扮演着非常重要的角色.异常处理并不是功能性需求,它需要优雅地处理任何错误情况,比如资源不可用.非法的输入.nul ...

  5. JAVA 异常处理机制

    主要讲述几点: 一.异常的简介 二.异常处理流程 三.运行时异常和非运行时异常 四.throws和throw关键字 一.异常简介 异常处理是在程序运行之中出现的情况,例如除数为零.异常类(Except ...

  6. Java异常处理和设计

    在程序设计中,进行异常处理是非常关键和重要的一部分.一个程序的异常处理框架的好坏直接影响到整个项目的代码质量以及后期维护成本和难度.试想一下,如果一个项目从头到尾没有考虑过异常处理,当程序出错从哪里寻 ...

  7. 深入理解java异常处理机制

       异常指不期而至的各种状况,如:文件找不到.网络连接失败.非法参数等.异常是一个事件,它发生在程序运行期间,干扰了正常的指令流程.Java通 过API中Throwable类的众多子类描述各种不同的 ...

  8. Java提高篇——Java 异常处理

    异常的概念 异常是程序中的一些错误,但并不是所有的错误都是异常,并且错误有时候是可以避免的. 比如说,你的代码少了一个分号,那么运行出来结果是提示是错误java.lang.Error:如果你用Syst ...

  9. java异常处理的设计

    有一句这样话:一个衡量Java设计师水平和开发团队纪律性的好方法就是读读他们应用程序里的异常处理代码. 本文主要讨论开发Java程序时,如何设计异常处理的代码,如何时抛异常,捕获到了怎么处理,而不是讲 ...

  10. java异常处理机制

    本文从Java异常最基本的概念.语法开始讲述了Java异常处理的基本知识,分析了Java异常体系结构,对比Spring的异常处理框 架,阐述了异常处理的基本原则.并且作者提出了自己处理一个大型应用系统 ...

随机推荐

  1. ActionMode 就记这么一点,不能更多了

    话说程序猿都是段子手,看到有的程序猿写文章,前面都会先写一个段子,我这么有幽默感的段子手,也决定效仿一下. "段子." 写完段子,下面开始进入正题. 今天要说的 ActionMod ...

  2. Linux(Ubuntu)下载安装破解Matlab2016

    跳过废话, 直接看教程 前言 笔者平常不是很喜欢用Matlab, 因为所需要的功能都能被Python替代, 而Matlab的肥和慢实在令人难以忍受. 在Linux系统下安装Matlab也比Window ...

  3. .bat文件设置IP、DNS

    这几天遇到个烦心事,每次开机之后都要去手动去设置一下IP地址,一大串的数字还是有点麻烦,于是就想写个批处理文件设置IP 注意:在DOS下设置IP时需要管理员权限运行 1.查看机子设置IP需要用到的名字 ...

  4. windows visual studio 2015安装

    下载 首先到微软官方下载在线安装文件 https://www.microsoft.com 运行安装 点击运行后选择 自定义 选择安装功能,把sql server去掉,是数据库插件 点击下一步,中途会提 ...

  5. CentOS 6.4 安装setuptools 和 pip

    若有报错信息:setuptools Compression requires the (missing) zlib module,需执行步骤1-2(否则可忽略) 1. 安装zlib 和 zlib-de ...

  6. .net是最牛逼的开发平台没有之一

    .net是最牛逼的开发平台没有之一 .net是最牛逼的开发平台没有之一 .net是最牛逼的开发平台没有之一 .net是最牛逼的开发平台没有之一 .net是最牛逼的开发平台没有之一 .net是最牛逼的开 ...

  7. R系列:关联分析;某电商平台的数据;做捆绑销售和商品关联推荐

    附注:不要问我为什么写这么快,是16年写的. 一.分析目的 I用户在某电商平台买了A,那么平台接下来应该给用户推荐什么,即用户在买了商品A之后接下来买什么的倾向性最大: II应该把哪些商品在一起做捆绑 ...

  8. Oracle 11g 删除归档日志

    工作环境ORACLE 11g 归档日志满了,手动删除出现 ora 异常. 注:工作环境是linux redhat 5 执行命令如下: 1. 进入rman 2. connect target / 3. ...

  9. angular : ngModel 内部流程

    angular 1.5 beta link NgModelController provides API for the ngModel directive. The controller conta ...

  10. 快速排序OC、Swift版源码

    前言: 你要问我学学算法在工作当中有什么用,说实话,当达不到那个地步的时候,可能我们不能直接的感觉到它的用处!你就抱着这样一个心态,当一些APP中涉及到算法的时候我不想给其他人画界面!公司的项目也是暂 ...