美文网首页
Effective Java(3rd)-Item76 尽量维持失

Effective Java(3rd)-Item76 尽量维持失

作者: 难以置信的优雅 | 来源:发表于2018-09-18 15:07 被阅读0次

      在对象抛出异常之后,通常希望对象仍然处于定义良好的可用状态,即使在执行操作时发生了故障。对于检查异常尤其如此,调用者希望从检查异常中恢复。一般来说,失败的方法调用应该使对象处于调用之前的状态。具有此属性的方法称为故障原子方法。
      有几种方法可以达到这种效果。最简单的方法是设计不可变对象( item17)。如果对象是不可变的,则失败原子性是免费的。如果一个操作失败,它可能会阻止创建一个新对象,但是它不会让一个现有对象处于不一致的状态,因为每个对象的状态在创建时是一致的,并且在创建后不能修改。

      对于操作可变对象的方法,实现故障原子性的最常见方法是在执行操作之前检查参数的有效性( item49 )。这导致大多数异常在对象修改开始之前被抛出。例如,考虑项目7的 Stack.pop 方法:

    image.png

      如果取消了初始大小检查,当该方法试图从空堆栈中弹出元素时,仍然会抛出异常。但是,这会使size字段处于不一致的(负)状态,导致以后对对象的任何方法调用失败。此外,pop方法抛出的ArrayIndexOutOfBoundsException将不适用于抽象(item73)。

      实现故障原子性的一个密切相关的方法是对计算进行排序,以便可能发生故障的任何部分都先于修改对象的任何部分发生。实现故障原子性的一个密切相关的方法是对计算进行排序,以便可能发生故障的任何部分都先于修改对象的任何部分发生。例如,考虑TreeMap的情况,它的元素按照一定的顺序排序。为了向TreeMap中添加元素,元素的类型必须能够使用树形图的顺序进行比较。试图添加一个不正确类型的元素将在以任何方式修改树之前,由于搜索树中的元素而导致ClassCastException失败。
      实现故障原子性的第三种方法是对对象的临时副本执行操作,并在操作完成后用临时副本替换对象的内容。当数据存储在临时数据结构中后,计算可以更快地执行时,这种方法自然会出现。例如,一些排序函数在排序之前将其输入列表复制到数组中,以降低访问排序内循环中的元素的成本。例如,一些排序函数在排序之前将其输入列表复制到数组中,以降低访问排序内循环中的元素的成本。
      实现故障原子性的最后一种不太常见的方法是编写恢复代码,拦截在操作过程中发生的故障,并使对象回滚到操作开始之前的状态。实现故障原子性的最后一种不太常见的方法是编写恢复代码,拦截在操作过程中发生的故障,并使对象回滚到操作开始之前的状态。
      虽然失败原子性通常是可取的,但它并不总是可以实现的。例如,如果两个线程试图在没有适当同步的情况下并发地修改同一个对象,那么该对象可能会处于不一致的状态。因此,假定在捕获a之后对象仍然可用是ConcurrentModificationException错误。错误是不可恢复的,所以在抛出AssertionError时,您甚至不需要尝试保存失败原子性。
      即使在原子性可能失败的情况下,也并不总是可取的。对于某些操作,它将显著增加成本或复杂性。也就是说,一旦意识到这个问题,就可以轻松地实现失败原子性。
      总之,作为规则,作为方法规范的一部分生成的任何异常都应该使对象保持在方法调用之前的状态。如果违反了这条规则,API文档应该清楚地指出对象将处于什么状态。不幸的是,许多现有的API文档都没有达到这个理想。
    本文写于2019.7.22,历时1天

    相关文章

      网友评论

          本文标题:Effective Java(3rd)-Item76 尽量维持失

          本文链接:https://www.haomeiwen.com/subject/kotpnftx.html