Tips

书中的源代码地址:https://github.com/jbloch/effective-java-3e-source-code

注意,书中的有些代码里方法是基于Java 9 API中的,所以JDK 最好下载 JDK 9以上的版本。

76. 争取保持失败原子性

在对象抛出异常之后,通常希望对象仍然处于定义良好的可用状态,即使失败发生在执行操作中。对于检查异常尤其如此,调用者希望从检查异常中恢复。一般来说,失败的方法调用应该使对象处于调用之前的状态。具有此属性的方法称为失败原子性( failure-atomic)。

有几种方法可以达到这种效果。最简单的方法是设计不可变对象(条目 17)。如果对象是不可变的,则失败原子性是必然的。如果一个操作失败,它可能会阻止创建一个新对象,但是它不会让一个现有对象处于不一致的状态,因为每个对象的状态在创建时是一致的,并且在创建后不能修改。

对于对可变对象进行操作的方法,实现失败原子性的最常用方法是:在执行操作之前检查参数的有效性(条目 49)。 这导致在对象修改开始之前就会抛出大多数异常。 例如,考虑条目 7中的Stack.pop方法:

public Object pop() {
if (size == 0)
throw new EmptyStackException();
Object result = elements[--size];
elements[size] = null; // Eliminate obsolete reference
return result;
}

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

实现失败原子性的一种密切相关的方法是对计算进行排序,以便任何可能失败的部分在修改对象的部分之前发生。 在执行部分计算时进行参数检查,此方法是前一个方法的自然扩展。 例如,考虑TreeMap的情况,其元素按照某种顺序排序。 为了向TreeMap添加元素,元素必须是可以使用TreeMap的顺序进行比较的类型。 在以任何方式修改tree之前,尝试添加错误键的元素自然会因为在tree中搜索元素失败而导致ClassCastException异常。

实现失败原子性的第三种方法是,在对象的临时拷贝上执行操作,并在操作完成后用临时拷贝替换对象的内容。当数据存储在临时数据结构中后,计算可以更快地执行时,这种方法自然会出现。例如,一些排序方法在排序之前将其输入列表拷贝到数组中,以降低访问排序内循环中的元素的成本。这样做是为了提高性能,但是作为一个额外的好处,它确保如果排序失败,输入列表保持不变。

实现失败原子性的最后的方法是,编写恢复代码(recovery code),但这种做法并不长用,该代码拦截在操作中发生的失败,并使对象将其状态回滚到操作开始之前的点。 此方法主要用于持久性的(基于磁盘)的数据结构。

虽然失败原子性通常是可取的,但它并不总是可以实现的。例如,如果两个线程试图在没有适当同步的情况下并发地修改同一个对象,那么该对象可能会处于不一致的状态。因此,如果假定在捕捉到ConcurrentModificationException之后对象仍然可用,那就错了。错误是不可恢复的,所以方法在抛出AssertionError时,甚至不需要尝试保存失败原子性。

即使在可能存在实现失败原子性的情况下,也并非总是可取的。 对于某些操作,它会显着增加成本或复杂性。 也就是说,一旦你意识到这个问题,通常都可以自由而轻松地做到失败原子性。

总之,作为规则,任何生成的异常都是方法规范的一部分,应该使对象处于方法调用之前的状态。 违反此规则的地方,API文档应清楚地指出该对象将保留在哪种状态。遗憾的是,许多现有的API文档无法实现这一理想。

Effective Java 第三版——76. 争取保持失败原子性的更多相关文章

  1. 《Effective Java 第三版》新条目介绍

    版权声明:本文为博主原创文章,可以随意转载,不过请加上原文链接. https://blog.csdn.net/u014717036/article/details/80588806前言 从去年的3月份 ...

  2. 《Effective Java 第三版》目录汇总

    经过反复不断的拖延和坚持,所有条目已经翻译完成,供大家分享学习.时间有限,个别地方翻译得比较仓促,希望有疑虑的地方指出批评改正. 第一章简介 忽略 第二章 创建和销毁对象 1. 考虑使用静态工厂方法替 ...

  3. Effective Java 第三版——49. 检查参数有效性

    Tips <Effective Java, Third Edition>一书英文版已经出版,这本书的第二版想必很多人都读过,号称Java四大名著之一,不过第二版2009年出版,到现在已经将 ...

  4. Effective Java 第三版——1. 考虑使用静态工厂方法替代构造方法

    Tips <Effective Java, Third Edition>一书英文版已经出版,这本书的第二版想必很多人都读过,号称Java四大名著之一,不过第二版2009年出版,到现在已经将 ...

  5. Effective Java 第三版——3. 使用私有构造方法或枚类实现Singleton属性

    Tips <Effective Java, Third Edition>一书英文版已经出版,这本书的第二版想必很多人都读过,号称Java四大名著之一,不过第二版2009年出版,到现在已经将 ...

  6. Effective Java 第三版——7. 消除过期的对象引用

    Tips <Effective Java, Third Edition>一书英文版已经出版,这本书的第二版想必很多人都读过,号称Java四大名著之一,不过第二版2009年出版,到现在已经将 ...

  7. Effective Java 第三版——9. 使用try-with-resources语句替代try-finally语句

    Tips <Effective Java, Third Edition>一书英文版已经出版,这本书的第二版想必很多人都读过,号称Java四大名著之一,不过第二版2009年出版,到现在已经将 ...

  8. Effective Java 第三版——10. 重写equals方法时遵守通用约定

    Tips <Effective Java, Third Edition>一书英文版已经出版,这本书的第二版想必很多人都读过,号称Java四大名著之一,不过第二版2009年出版,到现在已经将 ...

  9. Effective Java 第三版——11. 重写equals方法时同时也要重写hashcode方法

    Tips <Effective Java, Third Edition>一书英文版已经出版,这本书的第二版想必很多人都读过,号称Java四大名著之一,不过第二版2009年出版,到现在已经将 ...

随机推荐

  1. The more, The Better

    The more, The Better依赖背包+树形dpf[x][j+1]=max(f[x][j+1],f[x][j+1-k]+f[i->n][k]);我的一个疑问就是这k个节点会不会选重复, ...

  2. String、StringBuffer、StringBuilder的比较

    看String类的定义:public final class String...{private final char value[];} 看AbstractStringBuilder类的定义:abs ...

  3. shell编程第五天

  4. SpringBoot返回json和xml

    有些情况接口需要返回的是xml数据,在springboot中并不需要每次都转换一下数据格式,只需做一些微调整即可. 新建一个springboot项目,加入依赖jackson-dataformat-xm ...

  5. C# winform 弹出确认消息框

    if (MessageBox.Show("确认删除?", "此删除不可恢复", MessageBoxButtons.YesNo) == DialogResult ...

  6. async函数

    async函数的实现原理,就是将Generator函数和自动执行器,包装在一个函数里.async函数返回Promise对象,async函数的return值是then方法的参数,await后跟Promi ...

  7. Android EditText设置为Number类型后获取数字

    s_video_seg1 = Integer.parseInt(video_seg1.getEditableText().toString().trim()); 此处要使用getEditableTex ...

  8. BZOJ2689 : 堡垒

    问题等价于每个三角形里至少选择两个点. 考虑拓扑,每次取出度数为$2$的点$x$,代表一个只与最多一个三角形相邻的三角形$(x,y,z)$. 如果$x$已选,那么$(x,y)$以及$(x,z)$都已经 ...

  9. C++知识点:拷贝构造函数例子

    //拷贝构造函数: //函数参数传递时调用一次拷贝构造函数,给对象赋值时调用一次拷贝构造函数,对象作为参数传递后会被及时销毁. #include <fstream> #include &l ...

  10. django之视图获取用户请求相关信息以及请求头

    def index(request): print(type(request)) print(request.environ['HTTP_USER_AGENT'])#字典格式 print(reques ...