Java 编程思想学习笔记-异常

2019-06-18

本博客所有文章采用的授权方式为 自由转载-非商用-非衍生-保持署名 ,转载请务必注明出处,谢谢。

声明:
本博客欢迎转发,但请保留原作者信息!
博客地址:王超的博客;
内容系本人学习、研究和总结,如有雷同,实属荣幸!

12.2 基本异常

异常情形是指阻止当前方法或作用域继续执行的问题。 异常是的我们可以将每件事情都当成一个事务来考虑,而异常可以看护这些事务的底线。我们还可以将异常看成一种内建的恢复系统。如果程序某部分失败了,异常将恢复到程序中某个已知的稳定点。

12.2.1 异常参数

能够抛出任意类型的Throwable对象,它是异常类型的根类。通常,对于不同类型的错误,要抛出相应的异常。

12.3 捕获异常

监控区域是一段可能产生异常的代码,后面跟着处理这些异常的代码。

12.3.1 try 块

方法抛出异常,如果不希望方法结束,可以在方法内设置一个特殊的块来捕获异常,这个块称为try块。有了异常处理机制,可以把所有动作都放在try块里,然后只需要一个地方就可以捕获所有异常。 这意味着代码更容易编写和阅读,因为代码没有混在一起。

12.3.2 异常处理程序

异常处理程序必须紧跟在catch之后。只有匹配的catch字句才能得到执行。许多不同的调用可能产生相同的异常,但只需要一个针对此类型异常的处理程序。

异常处理理论有两种基本类型。Java支持终止模型,假设错误非常关键,以至于程序无法返回到异常发生的地方继续执行。另一种是恢复模型,修复错误,重新尝试调用出问题的方法。Java如果要实现类型恢复的行为,那么就不能抛出异常,而是调用方法来修正。或者try块放入循环中。

恢复模型开始显得非常吸引人,但是不实用。主要的原因是其导致的耦合:恢复性的处理程序需要了解异常抛出的地点,这必然需要包含依赖于抛出位置的非通用型代码。增加了代码编写和维护的困难。

12.4 创建自定义异常

定义自定义异常类,从已有的异常类继承,最好选择意思相近的异常类继承。

12.4.1 异常与记录日志

可以将日志的记录过程放在异常中,但是更多的情形是,异常处理生成日志消息,根据捕获的异常和额外信息。 可以加入额外的成员和构造器进一步的自定义异常。

12.5 异常说明

Java强制使用异常说明的语法,来告知客户端程序员某个方法可能抛出的异常类型。异常说明属于方法声明的一部分,紧跟在形参后面,throws后面跟所有可能抛出异常的列表。

12.6 捕获所有的异常

可以只写一个异常处理程序来捕获所有的异常,那就是补充异常类型的基类Exception。一般把这个处理程序放在末尾,以防其他捕获之前,已经被捕获了。

12.6.1 栈轨迹

getStackTrace返回一个栈轨迹元素构成的数组,栈顶是最后一个调用,栈底是第一个调用。

12.6.2 重新抛出异常

重抛异常会把异常抛给上一级的处理程序,同一个try块的其他异常处理程序将被跳过。 如果只是重抛异常,调用栈信息依旧是原异常抛出点。如果要更新这个信息,可以使用fillInStackTrace方法,它可以将当前的调用信息,替换原本的异常对象堆栈。 抛出新的异常,和fillInStackTrace效果相同,捕获程序仅知道新异常发生的堆栈。

12.6.3 异常链

常常在捕获异常后抛出另一个异常,并且希望把原始异常的信息保存下来,这称为异常链。

现在所有的Throwable的子类可以接收一个cause对象作为参数,这个cause就是原始异常。 在Throwable的子类,有三种子类提供了cause的构造对象,分别是Error,Exception和RuntimeException,其他需要通过initCause方法,而不是构造器。

12.7 Java 标准异常

12.7.1 特例:RuntimeException

RuntimeException类异常称为不受检查的异常,这种异常属于错误,将被自动捕获,也不需要在代码中进行异常说明。如果异常没有捕获,直达Main,将会自动调用异常的打印堆栈方法。

12.8 finally

对于一些代码,希望无论异常是否抛出,都会得到执行,这个时候就使用finally。

12.8.1 finally用来做什么

当要把除内存之外的资源恢复到初始状态时,就要用到finally子句。

finally保证无论异常是否被捕获,都可以获得执行。即使异常被抛出到上一层,也会得到执行。

当涉及到break和continue时,finally语句也会执行,如果结合标签使用,那么就可以达到和goto同样的效果。

12.8.2 在return 中使用finally

无论在何时return,finally语句均会执行。

12.8.3 异常丢失

异常作为程序出错的的标志,绝不应该被忽略,但使用特殊的方式使用finally语句,就可以发生这种状况。

要把所有可能抛出异常的方法,都放在try语句中。

另一种简单的丢失异常的方法是,在finally语句中return。

12.9 异常的限制

当覆盖方法的时候,只能抛出基类方法异常声明中声明的异常。这样限制很有用,因为这意味着当基类代码应用到派生类对象时,依旧可以工作,异常也不例外。

异常限制对构造器不起作用,派生类构造器可以抛出任何异常,但是必须包括基类构造器的异常说明。 派生类构造器不能捕获基类构造器的异常。

异常在继承和覆盖的过程中,不像方法,是缩小了,而不是放大了。

12.10 构造器

如果异常发生,所有东西都能被正确的清理吗? 大多数时候是安全的,但是涉及构造器就要非常小心。如果构造器内抛出了异常,这些清理行为也许就不能正常工作了。

这种通用的清理惯用法在构造器不抛出任何异常时也可以运用,其基本规则是: 在创建需要清理的对象之后,立刻进入一个try-finally语句块。

12.11 异常匹配

抛出异常时,会按照代码顺序找出最近的处理程序,匹配到后,就认为异常已经处理,不会继续查找。

查找的时候不要求一定要完全匹配,派生类的对象,也可以匹配基类的处理程序。

12.12 其他可选方式

异常处理系统像一个活门(trap door),放弃程序的正常执行序列。初衷是方便程序员处理错误。 异常处理原则是,只有在你知道如何处理的情况下才捕获异常。实际上,一个重要目标就是把错误处理的代码同错误发生地点分离,更容易理解和维护。

被检查的异常,强制你再可能还没准备好处理错误的时候,强制加上catch字句,这就导致了吞食则有害(harmful if swallowed)的问题。

程序员常常无意中吞食了异常,而这样的状况发生,虽然能通过编译,但是除非记得复查并改正代码,否则异常将会丢失。

12.12.3 把异常传递给控制台

main中使用Exception作为异常说明。

12.12.4 把”被检查的异常”转换为”不检查的异常”

可以将异常以RuntimeException包装后抛出。在最后捕获后,在使用getCause()获取原本的那个异常,然后进行处理。

12.13 异常使用指南

应该在下列情况下使用异常:

  1. 在恰当的级别处理问题。(在知道如何处理的情况下才捕获异常。)
  2. 解决问题并且重新调用产生异常的方法。
  3. 进行少量修复,然后绕过异常发生的地方继续执行。
  4. 用别的数据进行计算,以代替方法预计会返回的值。
  5. 把当前运行环境能做的事情尽量做完,然后把相同的异常重抛到更高层。
  6. 把当前运行环境能做的事情尽量做完,然后把不同的异常重抛到更高层。
  7. 终止程序。
  8. 进行简化。如果你的异常模式使问题变得太复杂,那用起来会非常痛苦烦人。
  9. 让类库和程序更安全。 这既是在位调试做短期投资,也是为程序的健壮性做长期投资。

文章评论

comments powered by Disqus


章节列表