异常

57,值针对异常的情况才适用异常

在现代jvm上实现上,基于异常的模式比标准模式要慢的多。

异常只能用于异常情况下,他们永远不用改用于正常的控制流。

设计良好的API不应该强迫它的客户端为了正常的控制流而适用异常。

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

java语言提供了三种可抛出结构:受检的异常,运行时异常和错误。

  • 如果期望调用者能够适当地恢复,对于这种情况应该使用受检异常。
  • 运行时异常和错误两者在行为上是等同的:它们都是不需要也不应该被捕获的可抛出结构。
  • 用运行时异常来表明编程错误。大多数运行时异常都表示前提违例,就是指没有遵守API规范建立的约定。如:数组下标错误。
  • 最好不要再实现任何新的Error子类。因此,你实现的所有未受检的抛出结构都应该是RuntimeException的子类(直接或间接的)。
例外:

考虑资源枯竭的情况,这可能是程序错误引起的,比如分配一块不合理的过大的数组,也可能是资源不足引起的。

因为受检异常往往指明了可恢复的条件,所以,对于这样的异常,提供一些辅助方法尤其重要,通过这些方法,调用者可以获得一些有助于恢复的西你想。

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

与返回代码不同,他们强迫程序员处理异常的条件,大大增强了可靠性。也就是说使用受检的异常会使API使用起来非常不方便。

如果正确使用API并不能阻止这中异常条件的产生,并且一旦产生异常,使用API的程序员可以立即采取有用过的方法,这种负担就被认为是正当的。

把受检异常变成未受检异常的一种方法是,把这个抛出异常的方法分成两个方法,其中一个方法返回一个boolean,表明是否应该抛出异常。

60,优先使用标准的异常

专家级程序员与缺乏经验的程序员一个最主要的区别在于,专家追求并且通常也能够实现高度的代码重用。

重用现有的异常有多方面的好处:
  • 最主要的好处是,它使你的API更加易于学习和使用,因为它与程序员已熟悉的习惯用法一致的。
  • 对于用到这些API的程序员而言,他们的可读性会更好,因为它们不会出现很多程序员不熟悉的异常。
  • 异常类越少,意味着内存印记(footparint)就越小,装载这些类的时间开销也越少。

在条件许可的条件下,其他的异常也可以被重用。如果某个异常能够满足你的需求,就不要犹豫,使用就是,不过,一定要确保抛出异常的条件与改异常的文档中描述的条件一致。而且如果希望稍微增加更过的失败——捕捉信息,可以放心地把现有的异常进行子类化。

61,抛出与抽象相对应的异常

更高层的实现应该捕获底层的异常,同时抛出可以按照高层抽象进行解释的异常。这种做法应该被称为异常转译。

有一种特殊的异常转译称为异常链,如果底层的异常对于调试导致高层异常的问题非常有帮助,使用异常链就很适合。

如果不能阻止或者处理来自更低层次的异常,一般做法是使用异常转译,除非低层次的方法碰巧可以保证它抛出的所有异常对高层也适合才可以将异常从底层传播到高层。

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

描述一个方法所抛出的异常,是正确使用这个方法时所需文档的重要组成部分。

  • 始终要单独声明受检的异常,并且利用javadoc的@throws标记,准确地记录下来抛出的每个异常条件。
  • 不要使用throws关键字将未受检的异常也包含在方法的声明中。
  • 如果一个类中的许多方法出于同样的原因而抛出同一个异常,在该类的文档注释中对这个异常建立文档,这是可以接受的。
63,在细节消息中包含能捕捉失败的异常

为了捕捉失败,异常的细节信息应该包含所有“对改异常有贡献”的参数和域的值。

为了确保在异常的细节消息中包含足够的能捕捉失败的信息,一种办法是在异常的构造器而不是字符串细节中引入这些信息。然后,有了这些信息,只要把它们放到消息描述中,就可以自动产生细节消息。

64,努力使失败保持原子性

当对象抛出异常之后,通常我们希望这个对象依然保持在一种定义良好的可用状态中,即使失败是发生在执行某个操作的过程中。

  • 最简单的方法莫过于设计一个不可变的对象。如果对象时不可变的,失败的原子性就是显然的。
  • 对于可变对象,最常见的办法是,在执行前检查参数的有效性。
  • 一种类似的获得失败原子性的方法是,调整计算处理过程的顺序,使得任何可能会失败的计算部分都在对象状态被修改钱发生。
  • 编写一段恢复代码,由它来拦截操作过程中发生的失败,以及对象回滚到操作开始之前的状态上。这种办法主要用于永久性的(基于磁盘的)数据结构。
  • 在对象的一份临时拷贝上执行操作,当操作完成之后再用临时拷贝中的结果代替对象的内容。

如果异常发生对象没有保持在改方法调用前,API文档应该清楚的指出将会处于什么状态。

65,不要忽略异常

要忽略一个异常非常简单,只需将方法调用通过try语句包围起来,并包含一个空的catch块。空的catch块会使异常达不到应有的目的

用空catch块忽略它,将会导致程序在遇到错误的情况下悄然地执行下去,然后可能在将来的某个点导致程序失败,却不能发现错误在哪。

正确地处理异常能够彻底挽回失败。只要将异常传播到外界,至少会导致程序迅速地失败,从而保留了有助于调试该失败条件的讯息。

你可能感兴趣的:(异常)