Java基础——如何优雅地处理异常

说起优雅 , 不知道为什么,我脑补的场景是这样的:
说起优雅的反面,不优雅 , 我首先想到的是这位:
希望费天王还能回归赛?。?伤病快点好,说回正题
1 基本概括2 主要介绍
2.1 异常处理机制
在 Java 应用程序中,异常处理机制有:抛出异常、捕捉异常 。
抛出异常
这里的“抛出异常”是指主动抛出异常 。在设计、编写程序时,我们可以预料到一些可能出现的异常,如 FileNotFoundException,有时候我们并不希望在当前方法中对其进行捕获处理,怎么办呢?抛出去 , 让调用方去处理,通过 throw 关键字即可完成 , 如:
throw new FileNotFoundException()
关于抛出异常,还有一个点需要补充,那就是声明可检查异常 。在设计程序的时候 , 如果一个方法明确可能发生某些可检查异常,那么,可以在方法的定义中带上这些异常,如此,这个方法的调用方就必须对这些可检查异常进行处理 。
声明异常
根据 Java 规范 , 如果一个 Java 方法要抛出异常,那么需要在这个方法后面用 throws 关键字明确定义可以抛出的异常类型 。倘若没有定义,就默认该方法不抛出任何异常 。这样的规范决定了 Java 语法必须强行对异常进行 try-catch 。如下的方法签名:
public void foo() throws FileNotFoundException { … }
暗含了两方面的意思:
第一,该方法要抛出 FileNotFoundException 类型的异常;
第二,除了 FileNotFoundException 外不能 , 还误导了错误的出处 。真正 的问题出在抛出NullPointerException处的数行之外,这之间有可能存在好几次方法的调用和类的销毁 。我们的注意力被这条小鱼从真正的错误处吸引了过来,一直到我们往回看日志才能发现问题的源头 。
既然readPreferences() 真正应该做的事情不是捕获这些异常,那应该是什么?看起来有点有悖常理,通常最合适的做法其实是什么都不做 , 不要马上捕获异常 。把责任交给 readPreferences()的调用者 , 让它来研究处理配置文件缺失的恰当方法,它有可能会提示用户指定其他文件,或者使用默认值 , 实在不行的话也 许警告用户并退出程序 。
把异常处理的责任往调用链的上游传递的办法,就是在方法的throws子句声明异常 。在声明可能抛出的异常时,注意越具体越好 。这用于标识出调用方法的程序需要知晓并且准备处理的异常类型 。例如,“延迟捕获”版本的readPreferences()可能是这样的:
public void readPreferences(String filename)throws IllegalArgumentException,FileNotFoundException, IOException{if (filename == null){throw new IllegalArgumentException(&
2.3 异常处理的建议
2.4.1 只针对不正常的情况才使用异常
建议:异常只应该被用于不正常的条件 , 它们永远不应该被用于正常的控制流 。通过比较下面的两份代码进行说明 。
2.4.2 对于可恢复的条件使用被检查的异常,对于程序错误使用运行时异常
运行时异常 — RuntimeException类及其子类都被称为运行时异常 。
被检查的异常 — Exception类本身 , 以及Exception的子类中除了&
区别:
2.4.3 避免不必要的使用被检查的异常
&
适用于&
第一,即使正确使用API并不能阻止异常条件的发生 。
第二,一旦产生了异常 , 使用API的程序员可以采取有用的动作对程序进行处理 。
2.4.4 尽量使用标准的异常
代码重用是值得提倡的,这是一条通用规则,异常也不例外 。重用现有的异常有几个好处:
它使得你的API更加易于学习和使用 , 因为它与程序员原来已经熟悉的习惯用法是一致的 。
对于用到这些API的程序而言 , 它们的可读性更好 , 因为它们不会充斥着程序员不熟悉的异常 。
异常类越少,意味着内存占用越小,并且转载这些类的时间开销也越小 。
Java标准异常中有几个是经常被使用的异常 。如下表格:
虽然它们是Java平台库迄今为止最常被重用的异常,但是 , 在许可的条件下,其它的异常也可以被重用 。例如,如果你要实现诸如复数或者矩阵之类的算术对象,那么重用ArithmeticException和NumberFormatException将是非常合适的 。如果一个异常满足你的需要,则不要犹豫,使用就可以,不过你一定要确保抛出异常的条件与该异常的文档中描述的条件一致 。这种重用必须建立在语义的基础上,而不是名字的基础上!
2.4.5 抛出的异常要适合于相应的抽象
如果一个方法抛出的异常与它执行的任务没有明显的关联关系,这种情形会让人不知所措 。当一个方法传递一个由低层抽象抛出的异常时 , 往往会发生这种情况 。这种情况发生时,不仅让人困惑,而且也&
例如 , 在Java的集合框架AbstractSequentialList的get()方法如下(基于JDK1.7.0_40):
public E get(int index) {try {return listIterator(index).next();} catch (NoSuchElementException exc) {throw new IndexOutOfBoundsException(&
listIterator(index)会返回ListIterator对象 , 调用该对象的next()方法可能会抛出NoSuchElementException异常 。而在get()方法中 , 抛出NoSuchElementException异常会让人感到困惑 。所以,get()对NoSuchElementException进行了捕获,并抛出了IndexOutOfBoundsException异常 。即 , 相当于将NoSuchElementException转译成了IndexOutOfBoundsException异常 。
2.4.6 每个方法抛出的异常都要有文档
要单独的声明被检查的异常,并且利用Javadoc的@throws标记,准确地记录下每个异常被抛出的条件 。
如果一个类中的许多方法处于同样的原因而抛出同一个异常 , 那么在该类的文档注释中对这个异常做文档,而不是为每个方法单独做文档,这是可以接受的 。
2.4.7 在细节消息中包含失败 — 捕获消息
简而言之,当我们自定义异常或者抛出异常时,应该包含失败相关的信息 。
当一个程序由于一个未被捕获的异常而失败的时候,系统会自动打印出该异常的栈轨迹 。在栈轨迹中包含该异常的字符串表示 。典型情况下它包含该异常类的类名,以及紧随其后的细节消息 。
2.4.8 努力使失败保持原子性
当一个对象抛出一个异常之后,我们总期望这个对象仍然保持在一种定义良好的可用状态之中 。对于被检查的异常而言,这尤为重要 , 因为调用者通常期望从被检查的异常中恢复过来 。
一般而言 , 一个失败的方法调用应该使对象保持在&
设计一个非可变对象 。
对于在可变对象上执行操作的方法,获得&
public Object pop() {if (size==0)throw new EmptyStackException();Object result = elements[–size];elements[size] = null;return result; }
与上一种方法类似,可以对计算处理过程调整顺序 , 使得任何可能会失败的计算部分都发生在对象状态被修改之前 。
编写一段恢复代码,由它来解释操作过程中发生的失败 , 以及使对象回滚到操作开始之前的状态上 。
在对象的一份临时拷贝上执行操作,当操作完成之后再把临时拷贝中的结果复制给原来的对象 。
虽然&
即使在可以实现&
总的规则是:作为方法规范的一部分 , 任何一个异常都不应该改变对象调用该方法之前的状态,如果这条规则被违反,则API文档中应该清楚的指明对象将会处于什么样的状态 。
2.4.9 不要忽略异常
当一个API的设计者声明一个方法会抛出某个异常的时候,他们正在试图说明某些事情 。所以 , 请不要忽略它!忽略异常的代码如下:
try {…} catch (SomeException e) {}
【Java基础——如何优雅地处理异常】空的catch块会使异常达不到应有的目的 , 异常的目的是强迫你处理不正常的条件 。忽略一个异常,就如同忽略一个火警信号一样 — 若把火警信号器关闭了,那么当真正的火灾发生时 , 就没有人看到火警信号了 。所以,至少catch块应该包含一条说明,用来解释为什么忽略这个异常是合适的 。
2.4.10 切忌使用空catch块
在捕获了异常之后什么都不做,相当于忽略了这个异常 。千万不要使用空的catch块,空的catch块意味着你在程序中隐藏了错误和异常,并且很可能导致程序出现不可控的执行结果 。如果你非常肯定捕获到的异常不会以任何方式对程序造成影响,最好用Log日志将该异常进行记录,以便日后方便更新和维护 。
2.4.11 注意catch块的顺序
不要把上层类的异常放在最前面的catch块 。比如下面这段代码:
2.4.12 不要将提供给用户看的信息放在异常信息里
2.4.13 避免多次在日志信息中记录同一个异常
只在异常最开始发生的地方进行日志信息记录 。很多情况下异常都是层层向上跑出的,如果在每次向上抛出的时候,都Log到日志系统中,则会导致无从查找异常发生的根源 。
2.4.14 异常处理尽量放在高层进行
尽量将异常统一抛给上层调用者,由上层调用者统一之时如何进行处理 。如果在每个出现异常的地方都直接进行处理,会导致程序异常处理流程混乱,不利于后期维护和异常错误排查 。由上层统一进行处理会使得整个程序的流程清晰易懂 。
2.4.15 在finally中释放资源
以上就是朝夕生活(www.30zx.com)关于“Java基础——如何优雅地处理异常”的详细内容 , 希望对大家有所帮助!

猜你喜欢