·本章学习目标
理解致命性错误Error和异常Exception的基本概念及可能发生错误或异常的情况。
了解异常的类体系结构,检查和非检查型异常。
了解异常类Throwable及其子类所处理的异常种类。
深入理解抛出异常和捕捉异常的概念。
掌握在程序中使用try-catch-finally语句结构处理异常的方法。
掌握自定义异常的方法和主动抛出异常的方法。
·本章学习内容
异常的概念及产生的原因
处理异常的try、catch和finally语句
描述异常分类
开发程序来处理自己的异常
4.1 错误与异常的概念
程序运行时难免出现一些非正常的现象,例如死循环、除数为0、文件不存在、内存溢出等等。这些非正常的现象分为错误Error和异常Exception两种。
1、错误Error
Error指的是致命性的错误,诸如死循环、递归无法结束、内存溢出、硬件设备错误(例如软驱损坏、打印机缺纸等)。这一类的错误只能在编程阶段解决,在程序运行时程序自身是无法自行处理的。
2、异常Exception
Exception又可译为例外,指那些可以在编程时预测并可在程序运行时处理的错误。例如当两个变量做除法运算时,可以预测作为除数的变量也可能其值为0,这样在程序中就可以事先安排一段程序对这种异常的情况进行处理。
虽然异常是非致命性的错误,但是如果程序不加以处理,就默认由Java虚拟机处理,Java虚拟机在输出一个异常信息后也会造成程序的非正常结束。所以编程时必须对可预料的异常进行处理。
3、异常对程序的影响
有些异常可以通过编译,其异常只在程序运行到造成异常的语句时发生,例如“除数为0”的异常就是这样。
也有的异常如果不加以处理就无法通过编译,例如在线程的run方法中就必须对InterruptedException异常进行编程处理,否则无法通过编译。
4.2 异常类的层次
异常类的层次结构如图所示 :
4.3 异常类
4.3.1 Throwable类
在Java中,一切异常都是用异常类Throwable的直接或间接派生的某个异常类的实例来处理的。
Throwable类有两个子类,为Error和Exception。
Exception类的子类分为两种,即属于RuntimeException类的子类和不属于RuntimeException类的子类,其中RuntimeException类子类的异常基本上都是由于编程时不严谨的逻辑错误造成的,例如要求用户输入数字时没有充分考虑到用户有可能输入非数字的字符、汉字等。编程时要充分估计到此类错误发生的可能性,并在程序中加以处理。
Error类的子类都有后缀Error,这些类处理的都是系统内部错误,这类错误是不可修复和处理的,只能被动地通知用户发生了何种错误。
下表列出了Throwable类的常用方法,由于所有的异常类都是从Throwable类继承而来的,所以所有的异常类,不论是Java提供的异常类还是自定义的异常类,都支持这些方法。
常用方法
|
解 释
|
public String getMessage()
|
返回一个与异常有关的字符串信息
|
public void printStackTrace()
|
打印显示异常抛出时的堆栈跟踪状态,并输出到标准错误流中
|
public String toString()
|
返回一个简短描述异常对象的字符串信息
|
4.3.2常用的异常类
要进行异常处理编程,就要了解常用的异常类,表8-2给出了Exception类的常用构造方法,下表给出了Exception类中的常用子类。
构造方法
|
解 释
|
public Exception()
|
创建一个新的异常类
|
public Exception(String message)
|
创建一个新的异常类并指定异常信息
|
异常类Exception的常用子类
异常类名
|
说明
|
AWTException
|
图形界面异常
|
ClassNotFoundException
|
未找到欲装载使用的类
|
InterruptedException
|
线程异常(例如睡眠、等待、被其他线程暂停)
|
IOException
|
输入、输出异常
|
|
FileNotFoundException
|
未找到指定的文件或目录
|
UnknownHostException
|
无法确定的主机IP地址
|
MalformedURLException
|
URL地址异常
|
NoSuchMethodException
|
未找到指定的方法
|
RuntimeException
|
运行时异常
|
|
ArithmeticException
|
算术异常(除数为0)
|
ClassCastException
|
强制类型转换异常
|
IllegalArgumentException
|
非法参数异常
|
|
NumberFormatException
|
数字格式异常
|
IndexOutOfBoundsException
|
索引异常
|
|
ArrayIndexOutOfBoundsException
|
数组下标越界
|
StringIndexOutOfBoundsException
|
字符串越界
|
NullPointerException
|
空指针异常(引用尚未分配内存空间的对象)
|
NegativeArraySizeException
|
数组负下标异常
|
SecurityException
|
违背安全原则异常(例如Applet欲读写文件)
|
4.4 异常的处理
4.4.1 try-catch-finally结构
Java异常处理是通过5个关键字(try、catch、throw、throws和finally)控制。被进行异常监控的代码放在try语句块中,如果异常发生,代码可以捕捉(catch)异常,并用相应的方式进行处理,处理完成后可以在finally语句块中定义异常处理结束后应该执行的代码。
4.4.2异常的几种处理格式
处理异常的基本方法是使用try-catch-finally结构,该结构的使用格式如下:
try{
<可能产生异常、需要进行异常处理的程序段>
}catch (ExceptionType1 e){
<对ExceptionType1异常进行处理的程序段>
}catch (ExceptionType2 e){
<对ExceptionType2异常进行处理的程序段>
}
……
finally{
<无论是否发生异常都要执行的程序段>
}
在以上格式中,try程序段是必须的,finally程序段可以没有,catch程序段至少要有一个,如果try程序段中可能有多种异常,就要有一一对应的多个catch程序段。对于那些不进行异常处理就不能通过编译的情况,当不想进行异常处理时也要写一个空的catch程序段,象下面的样子。
catch (ExceptionType e){ }
catch的参数中ExceptionType是异常类型,应为上表中所列的类或它们的父类,e是一个异常对象。
【例4.2】处理数组越界访问。
有时,单个代码段可能引起多个异常,这时必须定义多个catch子句,每个子句处理一种异常。异常引发是,一个catch语句被执行后,其他子句别忽略。
【例4.3】处理包含除0和数组溢出的多种异常。
【例4.5】运用嵌套try语句。
【例4.6】隐式嵌套try语句。
4.4.3主动抛出异常
当try程序段中发生异常时称为抛出异常,而由catch程序段捕获异常并进行处理。所以Java对异常的处理是按照“抛出——捕获”的方式进行的。除了在try程序段由系统抛出异常外,程序员也可以故意主动地抛出异常,这要使用throw语句,以下是该语句的用法:
throw ExceptionObject
其中ExceptionObject是一个异常对象,而这个异常对象最常用的是Exception,所以throw语句最常见的写法是:
throw new Exception("信息字符串");
【例4.7】抛出异常。
4.5 将异常交系统处理
有一些异常事先很难预料,或者虽然能够预料但是可能性太多致使难以处理,在这些情况下可以把异常交给系统处理。例如创建一个文件流对象时,可能会遇到“文件不存在”的异常,这时就可以把异常交给交给系统来处理,不再需要用户自行编写try-catch-finally结构的异常处理程序。
要把异常交系统处理,就必须在可能产生异常的方法声明中使用throws子语句来指定处理异常的异常类,若指定多个异常类,彼此用逗号隔开。具体的格式如下:
[修饰符] <返回值类型> <方法名> ([参数列表]) <throws 异常类>
例如创建一个文件输入流对象时可能产生“文件不存在”的异常,若把该异常交系统自动处理,程序可以这样来写:
import java.io.*;
public class aFileInputStream{
public static void main(String args[]) throws IOException{
FileInputStream file1=new FileInputStream("myfile.txt");
}
}
以上程序一旦发生异常,会自动调用系统的IOException类进行处理。除了把异常交系统处理之外,也可以用throws子语句来指定自定义的异常类来处理异常。
【例4.8】声明异常
4.6 自定义异常类
处理异常更灵活的方法是使用自定义的异常类。自定义的异常类通常都是继承Java的Exception类或其子类。
【例4.9】自定义异常类。
·本章小结
在本章中学习了异常的概念和处理异常的方法。要对常用的异常有深刻的印象,才能熟练地处理系统已定义的异常,而要正确地处理异常必须掌握try-catch-finally语句结构的用法。
对于系统事先没有定义的异常,需要用户通过继承Exception类自定义一个异常类,并实例化为一个异常对象,然后通过throw语句主动抛出自定义异常,从而调用自定义的异常对象处理异常,本章的程序“自定义异常类”就是使用自定义异常类处理异常的例子。
对于比较简单的情况,也可以把异常交系统自行处理。
·习题
1、什么是异常?简述Java的异常处理机制。
2、系统定义的异常与用户自定义的异常有何不同?如何使用这两类异常?
3、编写从键盘读入10个字符放入一个字符数组,并在屏幕上显示它们的程序。请处理数组越界异常。