zoukankan      html  css  js  c++  java
  • 受检异常和非受检异常

    java提供了三种异常类型,受检异常(checked exception)、运行时异常(runtime exception)、错误(error)。

    通常情况下,对于可恢复的情况,使用受检异常;如果不清楚是否可能恢复,则最好使用未受检异常。

    1.受检异常

    如果抛出的异常是可恢复的,同时我们也期望API的调用者捕获异常进行恢复处理,那么我们应该使用受检异常。

    受检异常会强迫API的使用者截获异常并恢复处理,或者进行声明继续抛出。

    自定义异常一般情况下声明为非运行时异常(受检异常)。

    2.非受检异常

    RuntimeException和它的子类、Error和它的子类都是非受检异常。

    3.使用异常的建议

    3.1 只对异常情况才使用异常

    不使用异常到正常流,比如终止循环等。设计良好的API不应该强迫客户端为了正常的控制流而使用异常。可以考虑提供“状态测试”方法,例如Iterator的next()方法有一个对应的“状态测试方法” hasNext()

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

    如果期望调用者能够适当地恢复,应该使用受检的异常。对于程序错误,则使用运行时异常。对于受检异常,可以提供一些辅助方法,通过这些方法调用者可以获得一些有助于恢复的信息。例如用户取款时余额不足,可以提供一个方法获取余额。

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

    当以下两个条件都成立时,才使用受检异常:

    1. 正确地使用API并不能阻止这种异常条件的产生(例如网络、文件)。
    2. 一旦产生异常,使用API的程序员可以立即采取有用的动作,这种负担被认为是正当的。

    把受检异常变成运行时异常的一种方法是,把这个抛出异常的方法分成两个方法,其中一个返回boolean,表明是否应该抛出异常。例如前述的hasNext()。

    3.4 优先使用标准的异常

    使用标准的异常至少有以下好处:

    1. API更加易于学习和使用
    2. 可读性更好
    3. 异常类越少,内存印迹(footprint)就越少,装载这些类的时间开销也越少。

    常见的可重用异常包括:

    1. IllegalArgumentException 非Null的参数值不正确
    2. IllegalStateException 对于方法调用而言,对象状态不合适。
    3. NullPointException
    4. IndexOutOfBoundsException
    5. ConcurrentModificationException 禁止并修改时,检测到对象的并发修改
    6. UnsupportedOperationException

    3.5 抛出与抽象相对应的异常

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

    如果低层的异常对于调试导致高层异常的问题非常有帮助,使用异常链就很合适。大多数标准异常都支持链的构造器,如果不支持,可以利用Throwable的initCause方法设置原因。

    将受检异常转为运行时异常,这是在诸如 Spring 之类的框架中用来减少使用受检异常的方式之一,大部分 JDBC 的受检异常都被包装进 DataAccessException 中,DataAccessException异常是一种非受检异常。这个最佳实践带来的好处是可以将特定的异常限制到特定的模块中,比如把 SQLException 抛到 DAO 层,把有意义的运行时异常抛到客户端层。

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

    始终要单独地声明受检的异常,并利用Javadoc的@throws标记,准确地记录下抛出每个异常的条件 。如果一个方法可能抛出多个异常,不要使用“快捷方式”,声明它们的某个父类。永远不要声明一个方法“throws Exception”,因为它不仅没有提供任何有用的信息,而且掩盖了其它可能抛出的异常。

    Java 提供了 throw 和 throws 关键字来抛出异常,在 javadoc 中可以用@throw 为任何可能被抛出的异常编写文档。如果你编写 API 或者公共接口,这就变得非常重要。当任何方法抛出的异常都有相应的文档记录时,就能潜在的提醒任何调用该方法的开发者。

    3.7 努力使失败保持原子性

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

    要实现失败原子性,最简单的办法是设计不可变对象。对于可变对象,可采取的办法包括:

    1. 执行操作前,检查参数的有效性。
    2. 调整计算处理的顺序,使任何可能会失败的计算部分在对象状态被修改之前发生。
    3. 编写一段恢复代码。(这种办法主要用于永久性的数据结构,如磁盘)
    4. 在对象的一份临时拷贝上执行操作,操作完成后再用临时拷贝中的结果替换对象的内容。

    3.8 在 finally 程序块中关闭或者释放资源

    这是 Java 编程中一个广为人知的最佳实践和一个事实上的标准,尤其是在处理网络和 IO 操作的时候。在 finally 块中关闭资源能保证无论是处于正常还是异常执行的情况下,资源文件都能被合理释放,这由 finally 语句块保证。从 Java7 开始,新增加了一项更有趣的功能:自动资源管理,或者称之为ARM块。尽管如此,我们仍然要记住在 finally 块中关闭资源,这对于释放像 FileDescriptors 这类资源至关重要,因为它在 socket 和文件操作中都会被用到。

    3.9 在堆栈信息中包含引起异常的原因

    Java 库和开源代码在很多情况下会将一种异常包装成另一种异常。这样记录和打印根异常就变得非常重要。Java 异常类提供了 getCause() 方法来获取导致异常的原因,这可以提供更多有关异常发生的根本原因的信息。这条实践对调试或排除故障大有帮助。在把一个异常包装成另一种异常时,记住需要把源异常传递给新异常的构造器。

    3.10 始终提供异常的有意义的完整信息

    异常信息是最重要的,在其中,你能找到问题产生的原因,因为这是出问题后程序员最先看到的地方。记得始终提供精确的真实的信息。例如,对比下面两条 IllegalArgumentException 的异常信息:

    message 1: “Incorrect argument for method” message 2: “Illegal value for ${argument}: ${value}

    第一条消息仅说明了参数是非法的或不正确的,但第二条消息包括了参数名和非法值,这对找到错误原因很重要。在编写异常处理代码的时候,应当始终遵循该 Java 最佳实践。

    3.11 记住异常的性能代价高昂

    需要记住的一件事是异常代价高昂,同时让代码运行缓慢。假如你有一个方法从 ResultSet 中进行读取,它经常会抛出 SQLException 而不是将 cursor 移到下一元素,这将会比不抛出异常的正常代码执行的慢的多。因此最大限度的减少不必要的异常捕捉,去修复真正的根本问题。不要仅仅是抛出和捕捉异常,如果你能使用 boolean 变量去表示执行结果,可能会得到更整洁、更高性能的解决方案。修正错误的根源,避免不必要的异常捕捉。

    3.12 避免空的 catch 块

    没有什么比空的 catch 块更糟糕的了,因为它不仅隐藏了错误和异常,同时可能导致你的对象处于不可用状态或者脏状态。空的 catch 块没有意义,除非你非常肯定异常不会以任何方式影响对象的状态,但在程序执行期间,用日志记录错误依然是最好的方法。这在 Java 异常处理中不仅仅是一个最佳实践,而且是一个最通用的实践。

    ps:使用异常建议来自于各位大神的总结,我只是个知识的搬运工

     

  • 相关阅读:
    Python-Collections模块之defaultdict
    Python-Collections模块之deque
    Python-Collections模块之NameTuple
    Python-Collections模块之OrderedDict
    @常见的远程服务器连接工具:Xshell与secureCRT的比较!!!(对于刚接触的测试小白很有帮助哦)
    告诉你:传统测试和敏捷测试的区别!!!(比较全的解答,一看便明白)
    @软件测试环境搭建的详细流程(初学者不要错过哦,很实用)!!!!
    Netlib文件转化为mps文件
    新设备关联Gitlab
    CMake与OpenMP
  • 原文地址:https://www.cnblogs.com/selinamee/p/6873961.html
Copyright © 2011-2022 走看看