前言
本文主要讨论Java中的违例控制,包括以下内容:
1)什么是违例控制
2)违例的概念
3)Java中违例的类层次结构
4)如何掷出和捕获违例
5)捕获以后如何处理违例
本文将通过深入以上细节,向你完整地展现Java中违例的全貌,使得你能够在以后的程序编写中得心应手地处理各种可能遭遇的情况。
什么是违例控制
简单地说,违例控制就是在程序中提供给你这样一种能力:
1)监视程序中的异常情况
2)当异常情况发生时,将控制权交给你自己编写的违例控制代码
违例控制的流程
在Java中,这些工作由以下关键字来完成:try,catch,throw,throws,finally,他们的基本代码结构如下:
try
{
//代码块
}
catch(ExceptionType e)
{
//此违例类型的控制代码
}
finally
{
//清除回收等工作
}
首先执行try中包含的代码块,如果遇到执行错误,程序掷出(throw)一特定类型的违例,你捕捉到此违例并转而执行catch中的违例控制代码。最后,无论程序是否产生违例都必须执行finally中的代码,其主要为一些变量清除、资源回收(1)等工作。
违例的类层次结构
首先让我们来看看Throwable类,Sun是这样来描述它的:
The Throwable class is the superclass of all errors and exceptions in the Java language. Only objects that are instances of this class (or one of its subclasses) are thrown by the Java Virtual Machine or can be thrown by the Java throw statement. Similarly, only this class or one of its subclasses can be the argument type in a catch clause.
Instances of two subclasses,Error and Exception,are conventionally used to indicate that exceptional situations have occurred. Typically, these instances are freshly created in the context of the exceptional situation so as to include relevant information (such as stack trace data).
从中我们可以看出:
1)在Java中,违例对象必然是从Throwable中衍生出来的一个类的实例。
2) Throwable包含两个直接子类Error(错误)和Exception(违例)。
3)我们可以创建自己的违例类,只要它是从Throwable或其子类中衍生出来即可(确切地讲应该是从Exception或其子类中衍生出来,本文不准备详细讨论如何创建自己的违例类,你可以参考相关的资料)。
Error类和Exception类
Error表示那些由于异常情况引起的严重错误,我们不应去捕获这类对象,它主要包括系统内部错误以及资源耗尽等情况。而Exception类表示那些你必须去捕捉并处理的情况。
检查违例(Checked Exceptions)和不检查违例(Unchecked Exceptions)
在Exception的子类中有一个非常重要的类:RuntimeException(2)。Java中将自它或者它的子类衍生出来的任何违例都称作“不检查违例”(Unchecked Exceptions),自其他Exception子类衍生出来的违例都称作“检查违例”(Checked Exceptions)(3)。
不检查违例包括的问题主要有:造型错误,数组越界存取,空指针访问等,这些问题一般来说都是你程序编写的问题。简单地说,不检查违例就是那些由编译器来检查而无需你程序中控制的违例。而检查违例是指那些你必须处理的违例,否则编译时会产生一个编译错误。你可以选择下面任意一种方法来处理它:
1)捕获违例:在try代码块后面紧跟catch处理代码块
2)声明违例:在方法签名中用throws来通告可能会产生违例
Throwable类的构造器和方法
上面我们已经提到throwable是所有违例的超类,在这里我们就来分析一下它。Throwable类有四个构造器方法:
Throwable()
Throwable(String message)
Throwable(String message,Throwable cause)
Throwable(Throwable cause)
后两个是在JDK1.4中新出现的方法,用来支持所谓的链式违例(chained exception)机制(4)。接着,我们来看看throwable的一些主要方法:
fillInStackTrace()
getStackTrace()
printStackTrace()
setStackTrace(StackTraceElement[] stackTrace)
这四个方法是用来处理StackTrace的,如果你对StackTrace不是很熟悉,你可以这样理解它:就是当程序由于运行时错误终止时你在屏幕上看到的那些东西。
getLocalizedMessage()
getMessage()
这两个方法提供了访问封装在违例对象里的消息的接口。
toString()
Throwable重载了Object类的toString方法,用来返回一个Throwable的简短描述。
所有的违例对象都继承了throwable类的以上方法,所以你可以在catch代码块中调用其中任意一个方法,比如你可以使用getMessage方法来显示违例的详细信息。
那到底违例是什么意思呢?
在此我们引用Campione,Walrath在《The Java Tutorial》(5)中的原话:
The term exception is shorthand for the phrase "exceptional event". It can be defined as follows: Definition: An exception is an event that occurs during the execution of a program that disrupts the normal flow of instructions."
每当一个方法中有异常情况发生,它就实例化一个违例对象并把控制权交给运行时系统来处理,这些工作都是由throw来完成的(也就是我们通常所说的掷出一个违例)。而且该违例对象一般保存有自己的类型以及违例发生时程序状态等信息。
处理违例
由上可以看出,是由运行时系统来负责找出处理违例的代码。
每当违例发生后,运行时系统就开始向后搜索合适的违例控制器(catch代码块),比较的标准是:违例控制器中的违例类型必须是产生的违例类型或其超类。如果直到程序的结尾也没有找到合适的控制器,程序自动终止。
违例控制的优点
相对于传统的错误处理机制,违例控制具有以下优点:
1)将错误处理代码和常规代码分开
2)将错误交给调用栈处理,这就是所谓“将事情交给最合适的人来完成”的思想
3)通过将违例进行分类,可以让我们很容易地看出错误的所在和原因
违例控制的更多细节
我们在前面说过,除了由Error类和RuntimeException类衍生出来的违例外,你必须在程序中控制(handle)或者声明(declare)所有可能被掷出的违例,也就是所有的检查违例都必须得到处理,否则编译器就会对你亮起红灯,拒绝编译。
捕获违例
选择捕获违例意味着你的程序中必须存在有catch程序块,而且参数ExceptionType的类型必须是被掷出违例的类型,或者是其某一继承链中的超类(supperclass)(6)。
声明违例
如果在方法中会产生检查违例,但你又未在此方法中提供此违例的违例控制,那么你就必须声明此方法可能会掷出某特定违例,利用关键字throws就可以达到此目的。语法结构为(只列出方法的签名signature部分):
methodName(paramType param) throws ExceptionType
到底方法中会掷出哪些违例呢?
它包括你的方法中本身代码掷出的违例,你调用的方法掷出的违例,甚至还包括你调用的方法中调用的其他方法掷出的违例,等等。总之,只要控制流还在你的方法范围内,所有掷出的违例都是你必须考虑的。
实例代码
/**
*
Title:Except1.java
*
Description: Tested using JDK1.4.0 under Win2000 Professional
* @author Mac
* @version 2002/9/9
*/
import java.lang.Thread;
class Except1
{
public static void main(String[] args)
{
Except1 obj = new Except1();
try
{ //begin try block
obj.myMethod();
}
catch(InterruptedException e)
{
System.err.println(“Handle exception here”);
} //end catch block
} //end main
void myMethod() throws InterruptedException
{
Thread.currentThread().sleep(1000);
} //end myMethod
} //end class Except1
在上面这个例子中,我们看到myMethod方法只是简单地声明自己会产生一个名为InterruptedException的违例(throws InterruptedException),而真正的处理违例部分延迟到了main方法(catch(InterruptedException e))中。运行这个例子,并回答以下问题,看你是否确实掌握了它。
1、 如果去掉所有的违例控制代码,会发生什么情况?按照提示一步步完善你的程序。
2、 方法myMethod中的违例是怎么引起的?main方法中呢?
3、 把catch(InterruptedException e)换成catch(Exception e)行不行?为什么?
捕获多个违例
此外,可在try代码块中捕获多个违例类型,并分别对每种类型加以控制,每种类型对应于一个独立的catch从句:
try