Effective 异常

异常

只针对异常的情况才使用异常

  • 异常应该只用于异常的情况下, 永远不应该用于正常的控制流.
    因为会导致代码难看, 性能降低, 隐藏真正的错误, 有bug, 难以维护.
  • 良好设计的API不应该强迫它的客户端为了正常的控制流而使用异常.
    如果类具有状态相关(state-dependent)的方法, 往往也应该有个状态测试(state-testing)方法.
    例如Iterator接口的next()方法状态相关, 相应的测试方法是hasNext().
    状态测试方法不适用的情形: 并发未同步可能会产生状态不一致; 检车工作重复执行了动作操作会有性能问题。
    也可以让“状态相关”的方法返回一个可识别的值, 比如null或者是零长度的optional的值.

对可恢复的情况使用受检异常, 对编程错误使用运行时异常

Java提供三种可抛出结构(throwable):

  • 受检异常(checked exception).
  • 运行时异常(run-time exception).
  • 错误(error).

决定使用受检异常或是非受检异常时, 主要的原则

  • 如果期望调用者能够适当地恢复, 对于这种情况就应该使用受检的异常。
    通过抛出受检的异常, 强迫调用者在一个catch子句中处理该异常, 或者将它传播出去。
    每个受检异常都是对API用户的一个潜在指示: 与异常相关联的条件是这个方法的一种可能的结果.
  • 用运行时异常(runtime exception)来表明编程错误。
  • 你实现的所有未受检的抛出结构都应该是RuntimeException的子类(直接的或者间接的).
    不要定义任何既不是checked exception又不是runtime exception的异常.

避免不必要地使用受检的异常

受检异常与返回吗和未受检异常的区别

  • 受检的异常强迫程序员处理异常的情况, 大大增强了可靠性。
  • 过分使用受检异常会使API使用起来非常不方便。
  • 如果使用API无法做的更好,那么未受检的异常可能更为合适。

可以返回一个空的optional(异常情况下的缺省值)消除受检异常。 但就是不能提供更多的信息.

优先使用标准的异常

使用标准异常的好处

  • API更易于学习和使用
  • 可读性更好
  • 异常类越少,内存占用越小,装载类的时间开销更小

常用的异常:

  • IllegalArgumentException 非null的参数值不正确
  • IllegalStateException 不适合方法调用的对象状态
  • NullPointerException 空指针
  • IndexOutOfBoundsException 数组越界
  • ConcurrentModificationException 禁止并发
  • UnsupportedOperationException 未定义

不要直接使用Exception, RuntimeException, Throwable, Error, 把这些类看作抽象的.

如果没有可用的参数值,就 抛IllegalStateException, 否则抛IllegalArgumentException.

抛出与抽象相对应的异常

更高层的实现应该捕获底层的异常, 同时抛出可以按照高层抽象进行解释的异常.异常转译(exception translation)

如果低层的异常对于调试导致高层异常的问题非常有帮助, 使用异常链将低层的异常(原因)被传到高层异常(exception chaining)

大多数标准的异常都有支持链的构造器, 对于没有的, 可以用Throwable.initCause()设置原因。

尽管异常转移与不加选择地从底层传递异常的做法相比有所改进,但是也不要滥用他。

可能的情形下, 最好的方法是能避免底层的异常, 确保底层方法成功。
如果底层异常的确不可避免, 让高层默默解决这些异常, 从而将高级别方法的调用者与低层问题进行隔离。建议打印日志方便后续问题分析。

每个方法抛出的异常都要有文档

始终要单独地声明受检的异常, 并且利用Javadoc的@throws标记, 准确地记录下抛出每个异常的条件.

虽然Java并不要求方法声明它可能会抛出的未受检异常, 但是仔细地为未受检异常建立文档是非常明智的, 因为它们有效描述了方法的前提条件.

使用Javadoc的@throws标签记录下一个方法可能抛出的每个异常, 但是不要使用throws关键字将未受检的异常包含在方法生命中

如果一个类中的许多方法出于同样的原因而抛出同一个异常,在该类的文档注释中对这个异常建立文档。

在细节消息中包含能捕获失败的信息

程序由于未被捕获的异常失败的时候, 会打印该异常的堆栈轨迹, 包含该异常的toString()结果: 通常包含类名和细节消息(detail message).

异常的细节信息应该包含对该异常有贡献的参数和域的值.

为了确保在异常的细节消息中包含足够的信息, 一种办法是在异常的构造器中引入这些信息, 然后只要把它们放到消息描述中, 就可以自动产生细节信息.

但是要注意一定不要包含敏感信息, 如密码, 加密秘钥等.

努力使失败保持原子性

失败原子性(failure atomic)
失败的方法调用应该使对象保持在被调用之前的状态.

不要忽略异常

你可能感兴趣的:(Java初学者,java,开发语言)