第 5 章Java语言异常处理 面向对象程序设计特别强调软件质量的两个方面:一是程序结构方面 的可扩展性与可重用性;二是程序语法与语义方面的可靠性。可靠性 (reliability)是软件质量的关键因素,一个程序的可靠性体现在两方面:一 是程序的正确性(corectnes),指程序的实现是否满足了需求;二是程序的 健壮性(robustnes),指程序在异常条件下的执行能力。 在Java程序中,由于程序员的疏忽和环境因素的变化,经常会出现异 常情况,导致程序非正常终止。为了及时有效地处理程序运行中的错误, Java语言在参考C++语言的异常处理方法和思想的基础上,提供了一套 优秀的异常处理(exceptionhandling)机制,就可以有效地预防错误的程序 代码或系统错误所造成的不可预期的结果发生。异常处理机制通过对程 序中所有的异常进行捕获和恰当的处理来尝试恢复异常发生前的状态,或 对这些错误结果做一些善后处理。异常处理机制能够减少编程人员的工 作量,增加程序的灵活性,有效地增强程序的可读性和可靠性。 5.概 1 述 在程序运行时打断正常程序流程的任何不正常的情况称为错误或异 常。可能导致异常发生的原因有许多,例如下列的情形: ● 试图打开的文件不存在; ● 网络连接中断; ● 空指针异常;例如对一个值为nul 的引用变量进行操作; 算术异常;例如除数为0,操作符越界等; ● ● 要加载的类不存在。 下面是一个简单的程序,程序中声明了一个字符串数组,并通过一个 for循环将该数组输出。如果不认真地阅读分析程序,一般不容易发现程 序中可能导致异常的代码。 1 46 Java 程序设计———基于JDK 9 和NetBeans 实现 【例5.1】 Java程序异常示例。 1 public class Test { 2 public static void main(String[]args) { 3 String friends[]={"Lisa", "Mary", "Bily"}; 4 for(int i=0;i<4;i++){ 5 System.out.println(friends[i]); 6 } 7 System.out.println("Normal ended."); 8 } 9 } 【运行结果】 Lisa Mary Bily Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: 3 at Test.main (Test.java: 5) 【分析讨论】 ● 程序Test.java能够通过编译,但运行时出现异常,导致程序的非正常终止。 ● 程序在执行for循环语句块时,前3次依次输出String类型的数组friends包含的 3个元素,即执行结果的前3行。但是在第4次循环时,由于试图输出下标为3的 数组元素,而数组的长度为3,从而导致数组下标越界。产生异常的是第5句,异 常类型是java.lang.ArrayIndexOutOfBoundsException,并且系统自动显示了有关 异常的信息,指明异常的种类和出错位置。 在Java程序中,由于程序员的疏忽和环境因素的变化,会经常出现异常情况。如果 不对异常进行处理,将导致程序非正常终止。为保证程序正常运行,Java语言专门提供 了异常处理机制。Java语言首先针对各种常见的异常定义了相应的异常类,并建立了异 常类体系,如图5.1所示。 其中,java.lang.Throwable类是所有异常类的父类。Java语言中只有Throwable类 及其子类的对象才能由异常处理机制进行处理。该类提供的主要方法包括检索异常相关 信息,以及输出显示异常发生位置的堆栈追踪轨迹。Java语言异常类体系中定义了很多 常见的异常,例如: ● ArithmeticException:算术异常,整数的除0操作将导致该异常发生,例如,inti= 10/0。 ● NullPointerException:空指针异常,当对象没有实例化时,就试图通过该对象的 变量访问其数据或方法。 ● IOException:输入/输出异常,即进行输入/输出操作时可能产生的各种异常。 ● SecurityException:安全异常,一般由浏览器抛出。例如,Applet在试图访问本地 文件、试图连接该Applet所来自主机之外的其他主机,或试图执行其程序时,浏览 器中负责安全控制的SecurityManager类都要抛出这个异常。 第 5 章 Java 语言异常处理 71 图5.aa异常类继承层次 1 Jv Java语言中的异常可分为如下两类。 ● 错误(Eror)及其子类:JVM系统内部错误、资源耗尽等很难恢复的严重错误、不 能简单地恢复执行,一般不由程序处理。 ● 异常(Exception)及其子类:其他因编程错误或偶然的外在因素导致的一般性问 题,通过某种修正后程序还能继续运行,其中还可以分为 ■RuntimeException(运行时异常):也称为不检查异常,是指因为设计或实现方 式不当导致的问题,例如,数组使用越界、算术运算异常、空指针异常等。也可以 说是程序员的原因导致的、本来可以避免发生的情况。正确设计与实现的程序 不应产生这些异常。对于这类异常,处理的策略是纠正错误。 ■ 其他Exception类:描述运行时遇到的困难,它通常由环境而非程序员的原因引 起,如文件不存在、无效URL等。这类异常通常是由用户的误操作引起的,可 以在异常处理中进行处理。 例5.1中的异常就属于RuntimeException。出错的原因是数组friends中只含有3 个元素,当for循环执行到第4次时,试图访问根本不存在的第4个数组元素friends[3], 因此出错。 5.异常处理机制 2 异常处理是指程序获得异常并处理,然后继续程序的执行。Java语言要求如果程序 中调用的方法有可能产生某种类型的异常,那么调用该方法的程序必须采取相应的动作 1 48 Java 程序设计———基于JDK 9 和NetBeans 实现 处理异常。异常处理机制具体有两种方式:一是捕获并处理异常;二是将方法中产生的 异常抛出。 5.2.1 捕获并处理异常 Java程序在执行过程中如果出现异常,将会自动生成一个异常对象,该对象包含了 有关异常的信息,并被自动提交给Java运行时系统,这个过程称为抛出异常。当Java运 行时系统接收到异常对象时,会寻找能处理这一异常的代码并把当前异常对象交给其处 理,这一过程称为捕获异常。如果Java运行时系统找不到可以捕获异常的方法,则运行 时系统将终止,相应的Java程序也将退出。 try-catch-finally语句用于捕获程序中产生的异常,然后针对不同的异常采用不同的 处理程序进行处理。try-catch-finally语句的基本语法如下: try { Java statements //一条或多条可能抛出异常的Java 语句 } catch(ExceptionType1 e) { Java statements //当ExceptionType1 类型的异常抛出后要执行的代码 } catch(ExceptionType2 e) { Java statements //当ExceptionType2 类型的异常抛出后要执行的代码 } [finally { Java statements //执行最终清理的语句,即无条件执行的语句 } ] try-catch-finally语句把可能产生异常的语句放入try{}语句块中,然后在该语句块 后跟一个或多个catch语句块,每个catch块处理一种可能抛出的特定类型的异常。在运 行时刻,如果try{}语句块产生的异常与某个catch语句处理的异常类型相匹配,则执行 该catch语句块。finally语句定义了一个程序块,可放于try{}和catch{}块之后,用于 为异常处理提供一个统一的出口,使得在控制流转到程序的其他部分以前,能够对程序的 状态进行统一的管理。不论在try{}语句块中是否发生了异常事件,finally块中的语句 都会被执行。finally语句是可选的,可以省略。 需要注意的是,用catch语句进行异常处理时,可以使一个catch块捕获一种特定类 型的异常,也可以定义处理多种类型异常的通用catch块。因为在Java语言中允许对象 变量上溯造型,父类类型的变量可以指向子类对象,所以如果catch语句块要捕获的异常 类还存在其子类,则该异常处理块就将可以处理该异常类以及其所有子类表示的异常事 件。这样一个catch语句块就是一个能够处理多种异常的通用异常处理块。例如,在下 列语句中,catch语句块将处理Exception类及其所有子类类型的异常,即处理程序能够 处理的所有类型的异常。 try { ... }catch(Exception e) { System.out.println("Exception caugth: "+e.getMessage()); } 第5 章 Java 语言异常处理 1 49 接下来看一看上述机制是如何处理例5.1中的问题的。 【例5.2】 采用try-catch-finally对例5.1中的RuntimeException进行异常处理。 1 public class Test2 { 2 public static void main(String[]args) { 3 String friends[]={"Lisa", "Mary", "Bily"}; 4 try{ 5 for(int i=0;i<4;i++) { 6 System.out.println(friends[i]); 7 } 8 }catch(ArrayIndexOutOfBoundsException e) { 9 System.out.println("Index error"); 10 } 11 System.out.println("\nNormal ended."); 12 } 13 } 【运行结果】 Lisa Mary Bily Index error Normal ended 【分析讨论】 ● 从输出结果可以看出,出现异常时参数类型匹配的catch语句块得到执行,程序输 出提示性信息后继续执行,并未异常终止,此时程序的运行仍处于程序员的控制之 下。那么,既然运行错误经常发生,是不是所有的Java程序也都采取这种异常处 理措施呢? 答案是否定的,Java程序异常处理的原则是: ① 对于Error和RuntimeException,可以在程序中进行捕获和处理,但不是必 须的。 ② 对于IOException及其他异常类,必须在程序中进行捕获和处理。 ● 例5.1和例5.2中的ArrayIndexOutOfBoundsException就属于RuntimeException, 一个正确设计和实现的程序不会出现这种异常,因此可以根据实际情况选择是否 需要进行捕获和处理。而IOException及其他异常则属于另外一种必须进行捕获 和处理的情况。 再看一个IOException 的例子。例5.3 的类CreatingList要创建一个保存5 个 Integer对象的数组链表,并通过copyList()方法将该链表保存到FileList.txt文件中。 【例5.3】 创建链表并将其保存到文件中(未加任何异常处理,存在编译错误)。 1 import java.io.*; 2 import java.util.*; 1 50 Java 程序设计———基于JDK 9 和NetBeans 实现 3 class CreatingList { 4 private ArrayList list; 5 private static final int size=5; 6 public CreatingList () { 7 list=new ArrayList(size); 8 for(int i=0;i methodName ([]) throws 其中,exceptionList可以包含多个异常类型,用逗号隔开。 例5.4是使用try-catch-finally语句实现例5.3的异常处理的。例5.5是采用异常处 理的第二种方式对例5.3进行改进。 【例5.5】 采用声明抛出异常的方法对例5.3进行异常处理。 1 import java.io.*; 2 import java.util.*; 3 class CreatingList{ 4 private ArrayList list; 5 private static final int size=5; 6 public CreatingList(){ 7 list=new ArrayList(size); 8 for(int i=0;i