Java程序设计 第6章 异 常 处 理
6.1 异常处理 6.1.1 异常的概念 异常(Exception)又称为例外、差错、违例等,是特殊的运行错误对象,对应着Java语言特定的运行错误处理机制。 在一些传统的语言(如C语言中),通过使用if语句来判断是否出现了例外,同时,调用函数通过被调用函数的返回值感知在被调用函数中产生的例外事件并进行处理。全程变量ErrNo常常用来反映一个异常事件的类型。但是,这种错误处理机制会导致不少问题,如: (1) 正常处理程序与异常处理程序的代码同样地处理,程序的可读性大幅度降低; (2) 每次调用一个方法时都进行全面、细致的错误检查,程序的可维护性大大降低; (3) 由谁来处理错误的职责不清,以致于造成大量的潜伏的问题,等等。 为了解决这些问题,Java通过面向对象的方法来处理异常。
在一个方法的运行过程中,如果发生了异常,则这个方法生成代表该异常的一个对象,并把它交给运行时系统,运行时系统寻找相应的代码来处理这一异常。我们把生成异常对象并把它提交给运行时系统的过程称为抛出(throw)异常。运行时系统在方法的调用栈中查找,从生成异常的方法开始进行回溯,直到找到包含相应异常处理的方法为止,这一个过程称为捕获(catch)一个异常。 Java的这种机制的另一项好处就是能够简化错误控制代码。
Throwable与Exception Java中定义了很多异常类,每个异常类都代表了一种运行错误,类中包含了该运行错误的信息和处理错误的方法等内容。Java的异常类都是java.lang.Throwable的子类。它派生了两个子类:Error(错误)和Exception(违例)。其中Error类,由系统保留;而Exception类则供应用程序使用。其中: Error:JVM系统内部错误、资源耗尽等严重情况,由系统保留; Exception:其他因编程错误或偶然的外在因素导致的一般性问题,例如:对负数开平方根;空指针访问;试图读取不存在的文件网络连接中断。 一般所说的异常,都是指Exception及其子类。因为,应用程序不处理Error类。 同其他的类一样,Exception类有自己的方法和属性。它的构造函数有两个: public Exception(); public Exception(String s);
6.1.2 捕获和处理异常 Java中的异常处理机制可以概括成以下几个步骤。 6.1.2 捕获和处理异常 Java中的异常处理机制可以概括成以下几个步骤。 (1)Java程序的执行过程中如出现异常,会自动生成一个异常类对象,该异常对象将被提交给Java运行时系统,这个过程称为抛出异常。抛出异常也可以由程序来强制进行。 (2)当Java在运行时系统接收到异常对象,会寻找能处理这一异常的代码并把当前异常对象交给其处理,这一过程称为捕获(catch)异常。 (3)如果Java运行时系统找不到可以捕获异常的方法,则运行时系统将终止,相应的Java程序也将退出。
抛出异常 捕获异常 throw异常对象; 语句组 }catch(异常类名 异常形式参数名){ 异常处理语句组; }finally{ } try{ 语句组 }catch(异常类名 异常形式参数名){ 异常处理语句组; }finally{ } 其中,catch语句可以有一个或多个,而且至少要有一个catch语句或finally语句
受检的异常 Exception类及其子对触,如果不是RuntimeException及其子对象,称为受检的异常(checked Exception),编译程序要求用户明确进行语法处理,即要么捕(catch),要么抛(throws)
多异常的处理 finally语句 在覆盖的方法中声明异常 由于异常对象与catch块的匹配是按照catch块的先后排列顺序进行的,所以在处理多异常时应注意认真设计各catch块的排列顺序。 子类异常要排在父类异常的前面 finally语句 在覆盖的方法中声明异常 在子类中,如果要覆盖父类的一个方法,若父类中的方法声明了throws异常,则子类的方法也可以throws异常 但不能抛出更一般的异常
6.2 创建用户自定义异常类 创建用户自定义异常时,一般需要完成如下的工作。 6.2 创建用户自定义异常类 创建用户自定义异常时,一般需要完成如下的工作。 (1) 声明一个新的异常类,使之以Exception类或其他某个已经存在的系统异常类或 用户异常类为父类。 (2) 为新的异常类定义属性和方法,或重载父类的属性和方法,使这些属性和方法能 够体现该类所对应的错误的信息。 例Exce6.java 用户定义的异常类
6.2.2 重抛异常及异常链接 对于异常,不仅要进行捕获处理,有时候还需要将此异常进一步传递给调用者,以便让调用者也能感受到这种异常。这时可以在catch语句块或finally语句块中采取以下三种方式: (1)将当前捕获的异常再次抛出。格式如下: throw e; (2)重新生成一个异常,并抛出,如: throw new Exception("some message"); (3)重新生成并抛出一个新异常,该异常中包含了当前异常的信息,如: throw new Exception("some message",e); 例:ExceptionCause.java
6.3 断言及程序的测试 6.3.1 使用assert assert的格式是: 6.3 断言及程序的测试 6.3.1 使用assert assert的格式是: assert 表达式; assert 表达式 : 信息; 在调试程序时,如果条件表达式不为true,则程序会产生异常,并输出相关的错误信息。 要注意的是,只有在JDK1.4及以上的版本中才可以使用断言。具体地说,在编译时,要通过-source选项来指明版本,如: javac -deprecation -source 1.4 -classpath . Assertion.java 在运行时,要使assert起作用,则在运行时,使用选项(-ea,即-enableassertions)。如: java -ea -classpath . Assertion
6.3.2 程序的测试及JUnit 在实际开发过程中,程序的修改是经常要进行的过程,例如实现某个功能原先有一个算法,后来又找到一个新的算法,在新的算法实现时,必须保证程序在修改后其结果仍然是正确的。在现代的开发过程中,一种重要的措施是使用测试。也就是说,在编写程序代码的同时,还编写测试代码来判断这些程序是否正确。有人更进一步地把这个过程称为“测试驱动”的开发过程。编写测试代码,表面上增加了代码量,但实际上,由于它保证了它在单元级别的正确性,从而保证了代码的质量,减少了后期的查错与调试的时间,所以实际上它提高了程序的开发效率。
在Java的测试过程,经常使用JUnit框架,它是一个开放源代码产品,它支持测试开发,并提供运行这些测试的环境。有关 JUnit 的详细信息,请参见http://www.junit.org。 以NetBeans IDE为例。要生成一个JUnit测试项目,只需要选择“工具”菜单中的“JUnit”“创建测试项目”即可。
为了运行测试,可以选择“运行”菜单中的“测试”,或者直接按Alt+F6即可。 在测试中常用的语句如下: fail( 信息 ); //表示程序出错 assertEqaul(参数1,参数2 ); //表示程序要保证两个参数要相等 assertNull(参数); //表示参数要为null 从以上语句可以看来,在一定意义上JUnit是对assert语句的极大的扩充,并提供了一个完整的单元测试框架。