第九章 违例差错控制
基本违例
产生一个违例时,发生的几件事情:
按照与创建Java对象一样的方法创建违例对象:在内存“堆”中,使用new创建
停止当前执行路径,从当前环境中释放违例对象的句柄
违例控制器接管一切,并开始查找一个恰当的地方(违例控制器),用于继续程序的执行
违例控制器的职责是从问题中恢复,使程序要么尝试另一条执行路径,要么简单地继续
在所有标准违例中,存在两个构建器:默认构建器,需一个字符串作为自变量的构建器
throw通过“掷”出一个违例,从原来的作用域中退出,但会先返回一个值,再退出方法或作用域;返回于一个恰当的“违例控制器”,距离违例“掷 ”出的地方可能相当遥远——在调用堆栈中要低上许多级
违例的捕获
如果在一个方法执行过程中发生违例,又不想直接throw离开方法,就设计一个特殊的代码块,用它来catch违例,即try块
生成的违例必须在某个地方终止,即违例控制器或违例控制模块,针对每种想捕获的违例类型,都必须有相应的违例控制器
控制器必须“紧接”try块后面,若“掷”出一个违例,违例控制机制会搜寻自变量与违例类型相符的第一个控制器,进入到那个catch从句中,只有相符的catch从句才会得到执行(与switch语句不同,switch在每个case后都需一个break命令,防止误执行其他语句)
中断:只要“掷”出一个违例,就表明没有办法补救错误,无法返回违例发生的地方
恢复:违例得到控制后仍想继续执行,此时违例更想一个方法调用
通过捕获基础类违例类型Exception可捕获所有类型的违例,在实际使用中最好将其置于控制器表的末尾,防止跟随其后的任何特殊违例控制器失效
在catch块中重新“掷”出违例,会导致违例进入更高一级环境的违例控制器中。如果只是简单的重新掷出当前违例,其有关的信息会与违例起源地对应,而不是与重新掷出它的地点对应;如果调用fillInStackTrace()后再掷出,会将当前堆栈信息填充到原来的违例对象中,即违例对象的相关信息会与重新掷出的地点对应
fillInStackTrace()会生成一个Throwable对象句柄(Throwable是Exception的一个基础类),所以Throwable类必须在违例规格中出现
从一个已捕获的违例中重新掷出一个不同的违例,与原违例起源地有关的信息会全部丢失,留下的是与新的throw有关的信息
标准Java违例
- Throwable包含两种常规类型:
- Error:编译期和系统错误,一般不必特意捕获他们
- Exception:从任何Java标准库的类方法中掷出的基本类型
- RuntimeException用于指出编程中的错误,几乎永远不必专门捕获,它在默认情况下会自动得到处理,可选择掷出一部分RuntimeException
- 假如一个RuntimeException获得到达main()的所有途径,同时不被捕获,那么当程序退出时,会为那个违例调用printStackTrace()
创建自己的违例
创建自己的违例,必须从一个现有的违例类型继承
违例的限制
原书示例:
1 | class Exception1 extends Exception {} |
用finally清除
完整的违例控制:
1 | try { |
- 无论是否掷出违例,finally从句都会执行
- 即使违例不在当前的catch从句集里捕获,finally都会在违例控制机制转到更高级别搜索一个控制器之前得以执行
违例匹配
“掷”出一个违例后,违例控制系统会按当初编写的顺序搜索“最接近”的控制器,一旦找到相符的控制器,就认为违例已得到控制,不再进行更多的搜索工作
在违例和它的控制器之间,并不需要非常精确的匹配,一个衍生类对象可与基础类的一个控制器相配
违例准则:
(1)解决问题并再次调用造成违例的方法
(2)平息事态的发展,并在不重新尝试方法的前提下继续
(3)计算另一些结果,而不是希望方法产生的结果
(4)在当前环境中尽可能解决问题,以及将相同的违例重新“掷”出一个更高级的环境
(5)在当前环境中尽可能解决问题,以及将不同的违例重新“掷”出一个更高级的环境
(6)中止程序执行
(7)简化编码。若违例方案使事情变得更加复杂,那就会令人非常烦恼,不如不用
(8)使自己的库和程序变得更加安全。这既是一种“短期投资”(便于调试),也是一种“长期投资”(改 善应用程序的健壮性)