JAVA讲义第6章.docx
《JAVA讲义第6章.docx》由会员分享,可在线阅读,更多相关《JAVA讲义第6章.docx(17页珍藏版)》请在冰点文库上搜索。
JAVA讲义第6章
第6章Java异常处理
6.1概述
异常就是在程序运行时由代码所产生的不正常状态。
换句话说,异常就是一个运行错误。
在不支持异常处理的计算机语言中,错误必须被人工进行检查和处理,这显然麻烦而低效。
Java语言提供了异常处理机制,为方法的异常终止和出错处理提供了清楚的接口。
异常(exception)也是异常事件(exceptionalevent)的简称,它是在程序运行中出现的违背指定正常流向的事件。
许多不同种类的错误可以导致异常,从严重的硬件错误到简单的编程错误。
当在Java方法中发生这样的错误时,方法将创建一个异常对象,并把它交给运行系统处理,运行系统负责找到处理错误的代码并执行之。
异常对象包括关于异常的信息、类型和错误发生时程序的状态以及对该错误的详细描述。
在Java中,创建一个异常对象并把它送到运行系统叫做抛出异常。
在抛出异常后,运行系统将寻找合适的方法来处理异常。
如果产生的异常类与所处理的异常类一致,则认为找到了合适的异常处理方法。
如果运行系统找遍了所有方法而没有找到合理的异常处理方法,则运行系统将终止Java程序的运行。
异常处理也叫做捕捉异常。
Java中的每个异常都是一个对象,它是java.lang包中Throwable类或其子类的实例对象。
Throwable类有两个子类:
Exception类和Error类。
Exception类是可以被捕捉并且可能恢复的异常类,也可以继承Exception类生成自己的异常类。
Error类一般被认为是不可恢复和不可捕捉的异常类。
6.2异常的类型
6.2.1异常类的层次结构
图6.1异常类的层次
java语言用继承的方式来组织各种异常。
所有的异常都是Throwable类或子类,而Throwable类又直接继承于object类。
Throwable类有两个子类:
Error类与Exception类。
Exception类又进一步细分为RuntimeException(运行异常)类和Non-RuntimeException(非运行异常)类。
图6.1显示了各异常之间的继承关系。
如果程序中可能产生非运行异常,就必须明确地加以捕捉并处理,否则就无法通过编译检查。
与非运行异常不同的是,错误(Error)与运行异常(RuntimeException)不需要程序明确地捕捉并处理。
6.2.2Exception类及其子类
Exception类分为RuntimeException(运行异常)类和Non-RuntimeException(非运行异常)类。
1.运行异常
运行异常表现在Java运行系统执行过程中的异常,它是指Java程序在运行时发现的由Java解释抛出的各种异常,包括算术异常、下标异常等等。
表6-1列出几种常见的异常。
表6-1 几种常见的运行异常
异常类名
功能说明
ArithmeticException
除数为零的异常
IndexOutOfBoundsException
下标越界异常
ArrayIndexOutOfBoundsException
访问数组元素的下标越界异常
StringIndexOutOfBoundsException
字符串下标越界异常
ClassCaseException
类强制转换异常
NullpointerException
当程序试图访问一个空数组中的元素,或访问一个空对象中的方法或变量时产生的异常
2.非运行时异常
非运行时异常是由编译器在编译时检测是否会发生在方法的执行过程中的异常。
非运行异常是Non-RuntimeException及其子类的实例,可以通过throws语句抛出。
Java在其标准包Java.lang、java.util、java.io、中定义的异常类都是非运行异常类。
表6-2列出了常见的非运行异常。
表6-2 非运行异常
异常类名
功能说明
ClassNotFoundException
指定类或接口不存在的异常
IllegalAccessException
非法访问异常
IOException
输入输出异常
FileNotFoundException
找不到指定文件的异常
ProtocolException
网络协议异常
SocketException
Socket操作异常
MalformedURLException
统一资源定位器(URL)的格式不正确的异常
6.2.3Error类及其子类
Error类定义了正常情况下不希望捕捉的错误。
在捕捉Error子类时要多加小心,因为它们通常在出现灾难性错误时被创建。
表6-3列出一些常见的Error异常类。
表6-3 Error异常类
异常类名
功能说明
LinkageError
动态链接失败
VirtualMachineError
虚拟机错误
AWTError
AWT错误
6.3java异常产生与捕捉
6.3.1产生异常
通过下面两个引例来认识异常的产生。
【引例1】
importjava.io.*;
classException1
{
publicstaticvoidmain(Stringargs[])
{
FileInputStreamfis=newFileInputStream("text");
intb;
while((b=fis.read())!
=-1)
{
System.out.print((char)b);
}
fis.close();
}
}
该引例的第6行表明在一个文件“text”上建立文件输入流。
运行此程序时,如果这个文件并不存在,就会产生异常。
另外,第8行是读此文件的内容,如果该文件不存在,则从此文件中读取数据时,也可能发生异常。
【引例2】
classException2
{
publicstaticvoidmain(Stringargs[])
{
inta=0,b=0;
a=36/b;
System.out.println("a="+a);
}
}
从该引例的第6行不难看出,除数为零。
因此,运行此程序时必然会发生除零溢出的异常事件,使程序无法继续运行。
上面的两个引例都有或可能有异常产生。
对于一些异常,在程序中必须对它进行处理,否则编译程序时会指出错误,如引例1;但对另一些异常,在程序中可以不做处理,而直接由运行系统来处理,如引例2。
6.3.2捕捉异常
一个方法中如果对某种类型的异常对象提供了相应的处理代码,则这个方法就可以捕捉对应的异常。
捕捉异常是通过try-catch-finally语句实现的。
其语法为:
try{
正常程序段,可能抛出异常;
}
catch(异常类1异常变量){
捕捉异常类1有关的处理程序段;
}
catch(异常类2异常变量){
捕捉异常类2有关的处理程序段;
}
……
finally{
退出异常处理程序段;
}
1.try
try用来监视它所在的程序块是否发生异常,如果发生异常就抛出它。
对于系统产生的异常或程序块中未用try监视所产生的异常,将一律被Java运行系统自动抛出它。
2.catch
catch用来捕捉try程序块所抛出的异常,并根据异常类型采取相应的方法进行处理。
每个try程序块后面紧接着一个或多个catch语句,用于捕捉try程序块中所抛出的一个或多个异常。
catch语句只需要一个形式参数,参数类型给出它能够捕捉的异常类型,这个类必须是Throwable的子类,运行时系统通过参数值把被抛出的异常对象传递给catch块,在catch块中是对异常对象进行处理的代码。
【例6.1】捕捉引例1中的异常。
importjava.io.*;
classException3
{
publicstaticvoidmain(Stringargs[])
{
try{
FileInputStreamfis=newFileInputStream("text");
intb;
while((b=fis.read())!
=-1)
{
System.out.print((char)b);
}
fis.close();
}
catch(FileNotFoundExceptione){
System.out.println(e);
}
catch(IOExceptione){
System.out.println(e);
}
}
}
这里使用了两个catch语句分别对try代码块中可能出现的两个异常事件进行处理。
第一种情况,如果要打开的文件“text”在当前目录中不存在,程序运行时会抛出一个FileNotFoundException异常对象,由于第一个catch语句与该异常类型匹配,所以程序将执行第一个catch语句,于是就会显示下面的信息:
java.io.FileNotFoundException:
text(系统找不到指定的文件。
)
一般地,程序中如果有多个catch语句,那么,若有一个catch语句的异常类与抛出的异常对象匹配,那么,此catch语句将被执行,其他catch语句将被忽略。
此例中,由于第一个catch语句首先得到匹配,第二个catch语句将不会被执行。
因此,在安排catch语句的顺序时,首先应该捕捉最特殊的异常,然后再逐渐一般化。
通常在指定所捕捉的异常类型时,应该避免选择最一般的类型(如Exception)。
否则,当异常事件发生时,程序中不能确切判断异常的具体类型并做出相应处理以从错误中恢复。
第二种情况,如果要打开的文件“text”在当前目录中存在,程序的运行结果会显示“text”文件中的内容。
3.Finally
捕捉异常的最后一步是通过finally语句为异常处理提供一个统一的出口,使得在控制流转到程序的其他部分以前,能够对程序的状态作统一的管理。
不论在try代码块中是否发生了异常事件,finally代码块中的语句都会被执行。
【例6.2】finally语句作用的例子。
importjava.io.*;
classException4
{
publicstaticvoidmain(String[]args)throwsIOException
{
FileInputStreamfis=null;
try{
fis=newFileInputStream("text");
System.out.println("text文件的内容是:
");
intb;
while((b=fis.read())!
=-1)
{
System.out.print((char)b);
}
}
catch(FileNotFoundExceptione){
System.out.println(e);
}
catch(IOExceptione){
System.out.println(e);
}
finally{
if(fis!
=null)
{
System.out.println("正在关闭文件...");
fis.close();
}
else
{
System.out.println("文件没有打开!
");
}
}
}
}
如果要打开的文件“text”在当前目录中不存在,程序的运行结果如下:
java.io.FileNotFoundException:
text
文件没有打开!
这种情况下,finally语句被执行。
如果要打开的文件“text”在当前目录中存在,且“text”文件中的内容为:
“letusbeginlearnjava.”,则程序的运行结果如下:
text文件的内容是:
letusbeginlearnjava.
正在关闭文件...
这种情况下,finally语句仍被执行。
4.抛出异常:
throw
throw语句明确地抛出一个异常。
throw语句的一般格式:
throw<异常对象实例>;
其中,<异常对象实例>必须是Throwable类或其子类的一个对象,简单数据类型不能用于异常。
throw语句会中断程序流的执行,其后的语句将不会被执行。
系统会检测离throw最近的try块,看是否存在与throw语句抛出的异常类型相匹配的catch语句。
如果找到匹配的catch语句,则控制转到catch语句中。
如果没找到,则检测下一个try块,并且一直检测下去。
如果到最后仍没有找到匹配的catch语句,这个程序将被中断。
下面的程序产生并抛出了一个异常,捕捉到这个异常的处理又抛出一个异常到外部调用方法的处理中。
【例6.3】利用throw语句抛出异常的例子。
publicclassException5
{
staticvoiddemoproc()
{
try{
thrownewNullPointerException("demo");//抛出一个异常
}
catch(NullPointerExceptione){
System.out.println("捕捉内部异常.");//捕捉demoproc内部异常
throwe;//抛出另一个异常
}
}
publicstaticvoidmain(String[]args)
{
try{
demoproc();
}
catch(NullPointerExceptione){
System.out.println("再次捕捉异常:
"+e);//再次捕捉异常
}
}
}
从程序结构中可以看到,main()方法创建了一个异常处理块,在demoproc()方法中创建了另一个异常处理块。
所以,这个程序有两次机会处理同一个错误。
执行此程序时,首先调用demoproc()方法。
在demoproc()中先抛出一个NullPointerException异常,由demoproc()内部的catch语句捕捉到,输出“捕捉内部异常.”字样后,再次抛出一个同样的异常。
由于该异常在demoproc()方法中不能被捕捉到,所以只能被main()方法中的catch语句捕捉到并处理之。
因此,程序的运行结果如下:
捕捉内部异常.
再次捕捉异常:
java.lang.NullPointerException:
demo
5.声明抛出异常throws
如果一个方法可能抛出一个异常而它自己又没有处理方法,那么异常处理的任务就交给调用者去完成,此时,必须在方法声明中包括throws子句,以便该方法的调用者捕捉该异常。
一个throws子句列出了一个方法可能抛出的异常类型。
包括Throws子句的方法声明的一般格式如下:
<类型说明>方法名(参数列表)throws<异常类型表>
{
方法体;
}
下面的程序试图抛出一个它不捕捉的异常,因为这个程序没有指定一个throws子句来声明抛出异常,所以程序不能编译通过。
【例6.4】无声明的抛出异常
publicclassException6
{
staticvoidthrowOne()
{
System.out.println("抛出内部异常。
");
thrownewIllegalAccessException("demo");
}
publicstaticvoidmain(String[]args)
{
throwOne();
}
}
为了使这个程序编译成功,需要进行两处修改。
首先,需要声明方法throwOne()抛出了IllegalAccessException异常。
然后,必须在main()中定义try-catch语句来捕捉这个异常。
修改的正确程序如下:
【例6.5】声明抛出异常
publicclassException7
{
staticvoidthrowOne()throwsIllegalAccessException
{
System.out.println("抛出内部异常。
");
thrownewIllegalAccessException("demo");
}
publicstaticvoidmain(String[]args)
{
try{
throwOne();
}
catch(IllegalAccessExceptione){
System.out.println("捕捉到异常为:
"+e);
}
}
}
运行结果如下:
抛出内部异常。
捕捉到异常为:
java.lang.IllegalAccessException:
demo
6.3.3创建自己的异常类
尽管Java的内部异常可以处理大多数的一般错误,但有时还要创建自己的异常类型来处理特定的情况。
实际上,定义自己的异常类非常简单,只需要定义一个Exception类的子类就可以。
Execption类并没有定义它自己的任何方法,它继承了Throwable类提供的方法,所以,任何异常,包括自己建立的异常,都继承了Throwabe定义的方法,也可以在自定义的异常类中覆盖这些方法中的一个或多个方法。
【例6.6】创建自己的异常类。
classMyExceptionextendsException{
intnum=0;
MyException(){
num++;
}
Stringshow(){
return"自己的异常,序号为:
"+num;}
}
classDemoExcep{
staticvoidDemo(intn)throwsMyException{
System.out.println("n="+n);
if(n<200){
System.out.println("没有异常产生!
");
return;
}else{
thrownewMyException();
}
}
publicstaticvoidmain(Stringargs[]){
try{
Demo(75);
Demo(250);
}catch(MyExceptione){
System.out.println("捕捉到异常为:
"+e.show());
}
}
}
程序运行结果如下:
n=75
没有异常产生!
n=250
捕捉到异常为:
自己的异常,序号为:
1
自己所创建的异常类,在使用上和Java所提供的异常类一样,因此,只要根据前面所介绍的异常使用方法,就可以使用自己创建的异常类了。
6.3.4应用实例
【例6.7】多种异常处理的实例。
classMultiExcep
{
staticvoidDemo(intn)
{
inta=0,b=0;
intarr[]=newint[5];
switch(n)
{
case0:
a=38/b;break;
case1:
arr[17]=23;break;
}
}
publicstaticvoidmain(Stringargs[])
{
inti;
for(i=0;i<2;i++)
{
try{
System.out.println("i="+i);
Demo(i);
}
catch(ArithmeticExceptione){
System.out.println("除数为零异常!
");
}catch(ArrayIndexOutOfBoundsExceptione){
System.out.println("数组下标越界异常:
"+e);
}
finally{
System.out.println("执行finally代码块!
");
}
}
}
}
在main()方法中循环执行两次,执行第一次循环时,调用Demo()方法中的第一个分支语句,产生除数为零的异常;执行第二次循环时,调用demo()方法中的第二个分支语句,会产生访问数组元素的下标越界异常。
程序运行结果如下:
i=0
除数为零异常!
执行finally代码块!
i=1
数组下标越界异常:
java.lang.ArrayIndexOutOfBoundsException
执行finally代码块!
习题
6.1.列出五种常见的异常。
6.2.试解释什么是抛出异常、什么是捕捉异常。
6.3.如果try块中没有发出异常,当try块结束执行时,控制会转移到哪里去?
6.4.使用finally块的主要原因是什么?
实训
技能训练目的要求:
1.熟悉Java语言异常的类型。
2.熟悉Java语言异常的产生。
3.掌握Java语言异常的处理。
技能训练内容:
1.将例6.1输入计算机,在下面三种情况下运行程序。
(1)“text”文件不存在,观察运行结果;
(2)“text”文件存在,观察运行结果;
(3)将两个catch语句交换位置,观察运行结果并分析原因。
2.将例6.2输入计算机,观察运行结果,并说明finally语句的作用。
3.模仿例6.6创建一个自己的异常类。
4.将例6.7输入计算机,观察运行结果。