Effective Java: 异常

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

简介

错误示例:

try{
    int i = 0;
    while(true){
        rang[i++].climb();
    }
}catch(ArrayIndexOutOfBoundsException e){}
  • 异常机制的设计初衷是用于不正常的情形,所以很少会有 JVM实现视图对他们进行优化
  • 把代码放在 try-catch块中反而阻止了现代 JVM本来可能要执行的某些优化.
  • 对数组进行遍历 的标准模式 并不会导致 冗余的检查.

而正确的写法,应该是:

for(Mountain m: range){
    m.climb();
}

错误的观点:

认为 VM对每次数组的访问都要检查越界情况,导致性能差.
使用 异常模式 可以提高性能

事实

在现代 JVM的实现中,基于异常的模式比标准模式 要慢得多,降低了性能.
基于异常模式会模糊代码意图,如果出现了bug,它还会掩盖了 bug.

小结

  1. 异常应该只用于异常情况下,不应该用于正常的控制流.
  2. 设计良好的API不应该强迫它的客户端为了正常的控制流而是用异常.

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

简介

Java 提供了三种可抛出结构: 受检异常,运行时异常和错误.

使用规范

  • 如果期望调用者能够适当的恢复,就应该使用受检异常.

通过抛出受检异常,强迫调用者处理异常

  • 运行时和错误都是不应该被捕获的可抛出结构

如果程序抛出这个,往往都是不可恢复的情形

  • 运行时异常表明 编程错误
  • 错误往往是被JVM保留用于标示资源不足,约定失败或者其他使程序无法继续执行的条件

小结

对于 可恢复 的情况,使用受检异常
对于程序错误,使用运行时异常.
受检异常,往往需要指明一个可恢复的条件.

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

简介

上一个条目说 使用受检的异常,调用者就需要在 catch中处理这个异常.
也就是说过分的使用 受检异常 会使API 使用起来非常不方便,因为需要在 一个或多个catch块中处理这个异常. 或者 thows这些异常

小结

  1. 如果即便合理的调用了API也会遇到异常情形,并且捕获异常之后能够进行一些有意义的操作,才应该使用checked exception,其他情况下都应该使用RuntimeException
  2. 如果一个方法会抛出checked exception,都可以将其拆分为两个方法,一个用于判断是否会抛出异常,另一部分用于处理正常情况,如果不符合约定,就抛出RuntimeException

60.优先使用标准的异常

Java 平台类库未受检异常

代码重用是值得提倡的,异常也不例外

重用现有的异常有多方面的好处:

  1. 使你的API更加易于学习和使用
  2. 对于用到这些API的程序而言,可读性更好
  3. 异常类越少,意味着内存 印记 就越小,装载这些异常时间开销也越少

常用的有IllegalArgumentException,IllegalStateException

小结

选择重用哪个异常,并不总是那么精确.没有严格的规则/.

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

简介

  • 异常转译
    更高层的实现应该捕获 低层的异常,同时抛出可以按照高层抽象进行解释的异常. 就是 异常转译
// Exception Translation
try {
  // Use lower-level abstraction to do our bidding
  ...
} catch(LowerLevelException e) {
  throw new HigherLevelException(...);
}
  • 异常链
    如果低层的异常对于调试导致高层异常的问题非常有帮助,就可以使用异常链
// Exception Chaining
try {
  // Use lower-level abstraction to do our bidding
  ...
} catch(LowerLevelException e) {
  throw new HigherLevelException(e);
}

大多数标准的异常 都支持 异常连的构造器
如果不支持,可以使用 Throwable#initCause来设置
异常连也不能乱用,最好的做法是, 在调用低层方法之前确保他们会成功执行,从而避免他们抛出异常

小结

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

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

简介

  1. 不要通过声明抛出多个异常的父类来实现抛出多种异常的效果。
  2. 要利用 javadoc@throws标记准确的记录下抛出受检异常的条件
  3. 不要使用@throws将未受检的异常也包含在方法的声明中
  4. 如果一个类的很多方法都抛出同一个异常,那么可以将文档放到class doc中,而不是method doc

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

简介

  • 为了捕获失败,异常的细节信息,应该包含所有”对该异常有贡献”的参数和域的值.
  • 异常类型的toString方法应该尽可能多地返回有关失败原因的信息.
  • 为异常的”失败捕获”信息提供一些访问方法是合适的.提供这样的方法对于受检异常,比未受检异常更为重要.

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

简介

当对象抛出异常后,我们期望这个对象仍然保持在一种定义良好的可用状态.

  1. 一般而言,失败的方法调用,应该使对象保持在被调用之前的状态
    具有这种属性的方法被称为具有失败原子性

创建具有失败原子性的方法

  1. 最 简单的方法 莫过于 设计一个不可变得对象,如果对象是不可变的,那显然是具有原子性的
  2. 在可变对象上,最常见的方法是: 在执行操作之前,检查参数的有效性,在对象的状态被改变之前抛出适当的异常,如:
public Object pop(){
    if(size ==0){
        throw new EmptyStackException();
        //...
    }
}

如果不在 开始进行检查,在执行过程中也会 抛出异常,然而会导致 size 域 保持在不一致的状态,导致后续调用都会失败

  1. 编写一段 恢复代码,由它来拦截操作过程中发生的失败,以及使对象回滚到操作之前的状态.
  2. 在对象的一份临时拷贝上执行操作,当操作完成之后再用临时拷贝中的内容替换对象中的内容

65.不要忽略异常

简介

这条看似是显而易见的.但是却常常被违反,因此不可以忽视它
设计者声明一个方法抛出异常,等于正在试图说明什么.

// Empty catch block ignores exception
try{}catch(Exception e){}
  1. 空的 catch块,会使异常达不到应有的目的.
  2. 至少,catch块也应该包含一条说明,说明为什么可以忽略这个异常.
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值