第十章: 异常
异常只在异常情况下使用
异常不应该用于流程控制:
- 现代的
jvm
设计并没有对异常进行优化设计. - 将代码放入
try-catch
模块,将组织jvm
对其进行优化.
对于使用异常控制流程的代码可以使用状态测试方法(比如iterator
的next
方法为状态测试方法,hasNext()
为依赖状态测试的方法),或者返回optional
空包含。
如果是并发访问并且没有额外的同步控制或者方法调用会改变对象状态,应该使用返回optonal
空包含;否则应该使用状态测试方法,状态测试方法具有更好的可读性,而且如果错误的使用也会被更容易的检测出来。
可恢复情况使用checked Exception
,对于不可恢复情况使用未受检查异常unchecked Exception
所有checked exception
都是RuntimeException
的子类(直接或者间接的继承)
异常应该提供一个获取异常详细信息的方法,比如:触发条件,如何恢复,特别是checked Exception
避免不必要的checked Exception
checked Exception
可以调高程序的可读性,但是过度使用也会给调用客户端带来痛苦,所以当程序出意外时候可以恢复但是又需要客户端必须要处理一些意外的情况,可以使用optional
. 如果optional
不能满足情况(无法提供额外的信息说明为什么不能执行),那么可以返回一个checked exception
.
使用标准异常(java内置)
异常 | 翻译 | 何时使用 |
---|---|---|
IllegalArgumentException | 参数错误 | 参数不符合要求 |
IllegalStateException | 状态错误 | 对象状态不符合调用该方法 |
NullPointerException | 空指针 | 参数为空 |
IndexOutOfBoundsException | 下标越界 | 数据或者集合下标超出长度 |
ConcurrentModificationException | 并发修改 | 并发修改一个对象使不被允许的 |
UnsupportedOperationException | 对象不支持该操作 | 对象不不具备该操作 |
这些是常用的异常,当然还有其他异常可以使用。
异常转译
高层调用将底层的异常包装起来,抛出更高层次的异常给调用者,避免调用者感到困惑.
异常链: 底层的异常信息对于调试有作用,此时高层的异常提供一个访问的方法获取底层异常的信息(比如: Throwable的getCause()
方法)
异常文档注释
注释添加@throws表明会抛出哪些异常.
异常信息包含细节信息
异常详细信息应该包含除去敏感信息的所有参数值.
失败的原子性
当抛出异常时候,应该保持使用的对象保持在调用之前的状态.
- 对象设计为
immutable
- 进行参数检查,在方法开始检查参数是否有效,如果不满足调用条件,提前抛出异常,保持对象的状态不被改变.
- 创建一个临时的拷贝对象进行操作,操作完成之后使用临时拷贝对象中的结果作为代替原来对象的内容,如果失败,原对象内容并不会被改变.
- 最不常用的方法,编写一段
recovery code
回滚对象状态.
后记
并没有关于自定义异常使用的建议,个人在写代码会使用自定义异常(BusinessException
关闭爬栈开关)作为方法调用的前置条件不满足时的异常(参数检查不通过,抛出异常).