第十五章 异常

要想创建健壮的系统,它的每一个构件都必须是健壮的。

异常概念

C++的异常处理机制基于 Ada,Java 中的异常处理则建立在 C++的基础之上(尽管看上去更像 Object Pascal)。

基本异常

异常参数

所有标准异常类都有两个构造器:一个是无参构造器;另一个是接受字符串作为参数,以便能把相关信息放入异常对象的构造器。

Throwable 是异常类型的根类。

自定义异常

对异常来说,最重要的部分就是类名。

异常与记录日志

对于异常类来说,getMessage() 方法有点类似于 toString() 方法。

异常声明

不过还是有个能“作弊”的地方:可以声明方法将抛出异常,实际上却不抛出。编译器相信了这个声明,并强制此方法的用户像真的抛出异常那样使用这个方法。这样做的好处是,为异常先占个位子,以后就可以抛出这种异常而不用修改已有的代码。在定义抽象基类和接口时这种能力很重要,这样派生类或接口实现就能够抛出这些预先声明的异常。

捕获所有异常

多重捕获

通过 Java 7 的多重捕获机制,可以将不同类型的异常使用“或”将它们组合起来,只在一个 catch 块中使用:

try {
x();
} catch (Except1 | Except2 | Except3 | Except4 e) {
process();
}

栈轨迹

printStackTrace() 方法所提供的信息可以通过 getStackTrace() 方法来直接访问,这个方法将返回一个由栈轨迹中的元素所构成的数组,其中每一个元素都表示栈中的一桢。元素 0 是栈顶元素,并且是调用序列中的最后一个方法调用(这个 Throwable 被创建和抛出之处)。数组中的最后一个元素和栈底是调用序列中的第一个方法调用。

重新抛出异常

只是把当前异常对象重新抛出,那么 printStackTrace() 方法显示的将是原来异常抛出点的调用栈信息,而并非重新抛出点的信息。要想更新这个信息,可以调用 filInStackTrace() 方法,这将返回一个 Throwable 对象,它是通过把当前调用栈信息填入原来那个异常对象而建立的。调用 fillInStackTrace() 的那一行就成了异常的新发生地了。

精准的重新抛出异常

Java 7 开始,允许抛出更具体的异常,即 catch 中捕捉的是父类,catch 块中可以抛出子类。

异常链

Throwable 的子类中,只有三种基本的异常类提供了带 cause 参数的构造器。它们是 Error(用于 Java 虚拟机报告系统错误)、Exception 以及 RuntimeException。如果要把其他类型的异常链接起来,应该使用 initCause0 方法而不是构造器。

DynamicFieldsException dfe = new DynamicFieldsException();
dfe.initCause(new NullPointerException());
throw dfe;

Java 标准异常

Throwable 这个 Java 类被用来表示任何可以作为异常被抛出的类。Throwable 对象可分为两种类型(指从 Throwable 继承而得到的类型):Error 用来表示编译时和系统错误(除特殊情况外,一般不用你关心);Exception 是可以被抛出的基本类型,在 Java 类库、用户方法以及运行时故障中都可能抛出 Exception 型异常。所以 Java 程序员关心的基类型通常是 Exception

异常的基本的概念是用名称代表发生的问题,并且异常的名称应该可以望文知意。异常并非全是在 java.lang 包里定义的;有些异常是用来支持其他像 util、net 和 io 这样的程序包,这些异常可以通过它们的完整名称或者从它们的父类中看出端倪。

特例:RuntimeException

RuntimeException 类型的异常(或者任何从 RuntimeException 继承的异常),也被称为“不受检查异常”。这种异常属于错误, 代表的是编程错误,将被自动捕获。

只能在代码中忽略 RuntimeException(及其子类)类型的异常,因为所有受检查类型异常的处理都是由编译器强制实施的。

异常限制

当覆盖方法的时候,只能抛出在基类方法的异常说明里列出的那些异常。

异常限制对构造器不起作用。

一个出现在基类方法的异常说明中的异常,不一定会出现在派生类方法的异常说明里。这点同继承的规则明显不同,在继承中,基类的方法必须出现在派生类里,换句话说,在继承和覆盖的过程中,某个特定方法的“异常说明的接口”不是变大了而是变小了——这恰好和类接口在继承时的情形相反。

Try-With-Resources 用法

Java 7 引入了 try-with-resources 语法。

try-with-resources 定义子句中创建的对象(在括号内)必须实现 java.lang.Autocloseable 接口,这个接口有一个方法,close()。当在 Java 7 中引入 AutoCloseable 时,许多接口和类被修改以实现它;查看 Javadocs 中的 AutoCloseable,可以找到所有实现该接口的类列表,其中包括 Stream 对象。

规范头中定义的每个对象都会在 try 语句块运行结束之后调用 close() 方法。

Java 5 中的 Closeable 已经被修改,修改之后的接口继承了 AutoCloseable 接口。所以所有实现了 Closeable 接口的对象,都支持了 try-with-resources 特性。

不能在资源规范头中定义了一个不是 AutoCloseable 的对象。

异常匹配

抛出异常的时候,异常处理系统会按照代码的书写顺序找出“最近”的处理程序。找到匹配的处理程序之后,它就认为异常将得到处理,然后就不再继续查找。

查找的时候并不要求抛出的异常同处理程序所声明的异常完全匹配。派生类的对象也可以匹配其基类的处理程序。

其他可选方式

异常处理的一个重要原则是“只有在你知道如何处理的情况下才捕获异常"。

所有模型都是错误的,但有些是能用的。

好的程序设计语言能帮助程序员写出好程序,但无论哪种语言都避免不了程序员用它写出了坏程序。

异常指南

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

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

最新文章

  1. Find Out What Your Entity Framework Query Is Really Doing
  2. UVA - 11987 Almost Union-Find[并查集 删除]
  3. sharepoint OWA问题解决
  4. 使用expect脚本语言写一键发布服务(代码发布、所有服务重启)
  5. Spring MVC常见的三种URL请求参数的处理
  6. T-SQL 批处理
  7. 关于TransactionScope出错:“与基础事务管理器的通信失败”的解决方法总结
  8. NodeJs多进程和socket.io通讯-DEMO
  9. MyBatis起步
  10. app后端设计(5)-- 表情的处理
  11. Mybatis下的sql注入
  12. 处理文件中的" M-BM- "特殊符号
  13. awk 同时指定多个分隔符
  14. mui 常用手势
  15. iis部署 .net core webapi
  16. Mybatis 搭配 阿里druid连接池 连接 oracle 或 mysql
  17. [转载]java正则表达式
  18. Extjs treePanel过滤查询功能【转】
  19. 对Python线程池
  20. linux 自启动方法

热门文章

  1. centos 安装samba
  2. lnamp完整版[linux+apache2.4+php5.6.6+mysql5.6]
  3. ppt打不开,显示发现文件中的内容有问题。可尝试修复此演示文稿
  4. 2.k8s资源清单
  5. MSSQL时间格式转换
  6. B1011. A+B 和 C
  7. 【leetcode】1137. N-th Tribonacci Number
  8. APP功能测试注意点
  9. 贪心整理&一本通1431:钓鱼题解
  10. js刷新当前页面的5种方式