通过先进的错误纠正与恢复机制,我们可以有效地增强代码的健壮程度。对我们编写的每个程序来说,错误恢复都属于一个基本的考虑目标。它在 Java 中显得尤为重要,因为该语言的一个目标就是创建不同的程序组件,以便其他用户(客户程序员)使用。为构建一套健壮的系统,每个组件都必须非常健壮。
java违例
和 Java 的其他任何对象一样,需要用 new 在内存堆里创建违例,并需调用一个构建器。在所有标准违例中,存在着两个构建器:第一个是默认构建器,第二个则需使用一个字串自变量,使我们能在违例里置入相关信息:
if(t == null)
throw new NullPointerException("t = null");
违例的捕获
若某个方法产生一个违例,必须保证该违例能被捕获,并获得正确对待。对于Java 的违例控制机制,它的一个好处就是允许我们在一个地方将精力集中在要解决的问题上,然后在另一个地方对待来自那个代码内部的错误。
为理解违例是如何捕获的,首先必须掌握“警戒区”的概念。它代表一个特殊的代码区域,有可能产生违例,并在后面跟随用于控制那些违例的代码,try若位于一个方法内部,并“掷”出一个违例(或在这个方法内部调用的另一个方法产生了违例),那个方法就会在违例产生过程中退出。若不想一个 throw 离开方法,可在那个方法内部设置一个特殊的代码块,用它捕获违例。这就叫作“ try 块”,因为要在这个地方“尝试”各种方法调用。 try 块属于一种普通的作用域,用一个 try 关键字开头:
try {
// 可能产生违例的代码
}
若用一种不支持违例控制的编程语言全面检查错误,必须用设置和错误检测代码将每个方法都包围起来——即便多次调用相同的方法。而在使用了违例控制技术后,可将所有东西都置入一个try 块内,在同一地点捕获所有违例。这样便可极大简化我们的代码,并使其更易辨读,因为代码本身要达到的目标再也不会与繁复的错误检查混淆。
违例的中断与恢复
在违例控制理论中,共存在两种基本方法。在“中断”方法中( Java 和 C++提供了对这种方法的支持) ,我们假定错误非常关键,没有办法返回违例发生的地方。无论谁只要“掷”出一个违例,就表明没有办法补救错误,而且也不希望再回来。
另一种方法叫作“恢复”。它意味着违例控制器有责任来纠正当前的状况,然后取得出错的方法,假定下一次会成功执行。若使用恢复,意味着在违例得到控制以后仍然想继续执行。在这种情况下,我们的违例更象一个方法调用—— 我们用它在 Java 中设置各种各样特殊的环境,产生类似于“恢复”的行为(换言之,此时不是“掷”出一个违例,而是调用一个用于解决问题的方法)。另外,也可以将自己的try 块置入一个while 循环里,用它不断进入 try 块,直到结果满意时为止
如何捕获所有的违例
我们可创建一个控制器,令其捕获所有类型的违例。具体的做法是捕获基础类违例类型Exception(也存在其他类型的基础违例,但 Exception 是适用于几乎所有编程活动的基础)。如下所示:
catch(Exception e) {
System.out.println("caught an exception");
}
这段代码能捕获任何违例,所以在实际使用时最好将其置于控制器列表的末尾,防止跟随在后面的任何特殊违例控制器失效。
清除违例
永远不必关心如何清除前一个违例,或者与之有关的其他任何违例。它们都属于用new 创建的、以内存堆为基础的对象,所以垃圾收集器会自动将其清除。
Java 包含了一个名为 Throwable 的类,它对可以作为违例“掷”出的所有东西进行了描述。 Throwable 对象有两种常规类型(亦即“从 Throwable 继承”)。其中, Error 代表编译期和系统错误,我们一般不必特意捕获它们(除在特殊情况以外)。 Exception 是可以从任何标准 Java 库的类方法中“掷”出的基本类型。此外,它们亦可从我们自己的方法以及运行期偶发事件中“掷”出。
在java中若对一个空句柄发出了调用, Java 会自动产生一个 NullPointerException 违例。所以上述代码在任何情况下都是多余的。一个 RuntimeException(或者从它继承的任何东西)属于一种特殊情况,因为编译器不要求为这些类型指定违例规范。
假若一个 RuntimeException 获得到达 main()的所有途径,同时不被捕获,那么当程序退出时,会为那个违例调用 printStackTrace()。
破坏器
“破坏器”( Destructor)是“构建器”( Constructor)的反义词。它代表一个特殊的函数,一旦某个对象失去用处,通常就会调用它。
finally
无论一个违例是否在 try 块中发生,我们经常都想执行一些特定的代码。对一些特定的操作,经常都会遇到这种情况,但在恢复内存时一般都不需要(因为垃圾收集器会自动照料一切)。为达到这个目的,可在所有违例控制器的末尾使用一个 finally 从句。
在没有“垃圾收集”以及“自动调用破坏器”机制的一种语言中, finally 显得特别重要,因为程序员可用它担保内存的正确释放—— 无论在 try 块内部发生了什么状况。但 Java 提供了垃圾收集机制,所以内存的释放几乎绝对不会成为问题。除将内存设回原始状态以外,若要设置另一些东西, finally 就是必需的。即使违例不在当前的 catch 从句集里捕获, finally 都会在违例控制机制转到更高级别搜索一个控制器之前得以执行。
违例的准则
用违例做下面这些事情:
(1) 解决问题并再次调用造成违例的方法。
(2) 平息事态的发展,并在不重新尝试方法的前提下继续。
(3) 计算另一些结果,而不是希望方法产生的结果。
(4) 在当前环境中尽可能解决问题,以及将相同的违例重新“掷”出一个更高级的环境。
(5) 在当前环境中尽可能解决问题,以及将不同的违例重新“掷”出一个更高级的环境。
(6) 中止程序执行。
(7) 简化编码。若违例方案使事情变得更加复杂,那就会令人非常烦恼,不如不用。
(8) 使自己的库和程序变得更加安全。这既是一种“短期投资”(便于调试),也是一种“长期投资”(改
善应用程序的健壮性)