ch06.docx
《ch06.docx》由会员分享,可在线阅读,更多相关《ch06.docx(21页珍藏版)》请在冰豆网上搜索。
ch06
第6章Java异常处理
任何一个软件在运行中都会出现故障,在故障出现后需要有较为及时且有效的处理办法。
Java语言的异常处理机制是由捕获异常和处理异常两部分组成的。
本章主要介绍Java语言的异常处理机制。
本章要点
●异常和异常分类
●Java异常处理机制
●自定义异常
本章难点
●Java异常处理机制
学习目标
●理解Java异常处理机制。
●了解自定义异常。
6.1异常处理概述
6.1.1异常及其分类
任何计算机语言的程序都难免有漏洞,捕获错误最理想的是在编译期间,然而,在实际的程序设计中,并非所有错误都能在编译期间侦测到。
例如,下列情况在编译时通常检测不到:
类文件丢失、想打开的文件不存在、网络连接中断、操作数超出预定范围等。
这些在程序执行中,中断正常程序流程的异常条件称为错误或异常。
Java中,所有的异常都由类来表示。
所有的异常类都是从一个名为Throwable的类派生出来的。
因此,当程序中发生一个异常时,就会生成一个异常类的某种类型的对象。
Throwable有两个直接子类:
Error和Exception。
Error类表示程序运行时发生的严重错误,Exception类表示程序运行时产生的异常。
Error和Exception类又派生了很多子类用来表示程序运行产生的具体错误和异常。
例如使用NoClassDeFormatError表示没有类定义所产生的错误,使用ArithmeticException类表示算术运算产生的异常等。
Java中异常及其类的层次结构如图6-1所示:
图6-1异常类的层次结构
异常类Exception的构造函数及常用方法:
①Exception()
建立一个没有详细信息的异常。
②Exception(Strings)
建立一个有详细信息的异常,当异常被捕获时,可以从信息当中得知错误发生的原因。
③获得异常的有关信息
publicStringtoString():
返回描述异常类信息的字符串。
publicvoidprintStackTrace():
在当前标准输出当前异常对象的堆栈使用轨迹。
publicStringgetMessage():
输出错误的信息。
6.1.2异常处理机制
程序运行所导致的异常发生后,由Java语言提供的异常处理机制处理,Java异常处理机制由捕获异常和处理异常两部分组成。
在Java程序的执行过程中,如果出现了异常事件,就会生成一个异常对象。
生成的异常对象将传递给Java运行时系统,这一异常的产生和提交过程称为抛出(throw)异常。
当Java运行时系统得到一个异常对象时,它将会寻找处理这一异常的代码。
找到能够处理这种类型的异常的方法后,运行时系统把当前异常对象交给这个方法进行处理,这一过程称为捕获(catch)异常。
如果Java运行时系统找不到可以捕获异常的方法,则运行时系统将终止,相应的Java程序也将退出。
6.1.3Java中的常用异常类
1.执行异常
执行异常即运行时异常,继承于RuntimeException。
Java编译器允许程序不对它们做出处理。
下面列出了主要的运行时异常:
ArithmeticException:
一个不寻常算术运算产生的异常。
ArrayIndexOutOfBoundsException:
数组索引超出范围所产生的异常。
ClassCastExcption:
类对象强迫转换造成不当类对象所产生的异常。
NumberFormatException:
字符串转换数值所产生的异常。
IndexOutOfBoundsException:
索引超出范围所产生的异常。
NegativeException:
数组建立负值索引所产生的异常。
NullPointerException:
对象引用参考值为null所产生的异常。
2.检查异常
除了执行异常外,其余的Exception子类属于检查异常类,也称为非运行时异常。
Java编译器要求程序必须捕获或者声明抛出这种异常。
下面列出了主要的检查异常:
ClassNotFoundException:
找不到类或接口所产生的异常。
IllegaAccessException:
类定义不明确所产生的异常。
InterruptedException:
目前线程等待执行,另一线程中断目前线程所产生的异常。
【例6_1】ArithmeticException异常抛出示例
publicclassExam6_1{
publicstaticvoidmain(String[]args){
inti=10/0;
System.out.println(i);
}
}
程序运行时,由于使用10/0对i进行附值,除数为零,从而抛出算术异常,即ArithmeticException类的异常对象。
程序结果如图6-2所示。
图6-2例6_1程序运行结果
6.2Java异常的处理方法
为了写出健壮的Java程序,当程序出现异常时就应当进行处理,Java程序对异常处理有两种方式:
①通过try{}catch(){}finally{}块处理异常。
把可能会发生异常的程序代码放在try区块中,那么当程序执行发生异常时,catch区块会捕捉这个异常,并且以区块内的程序代码来处理异常,而finally区块则负责处理一些必要的工作,请注意,无论try区块内的程序代码是否发生异常finally区块内的程序代码都会被执行。
②将异常抛给上一层调用它的方法,由上一层方法进行异常处理或继续向更上一层方法抛出该异常。
6.2.1try/catch/finally
1.try/catch
异常处理的核心是try和catch。
这两个关键字要一起使用,只有try而没有catch,或者只有catch而没有try都是不可以的。
下面是try/catch异常处理代码块的基本形式:
try//监视
{
可能发生异常的代码块;
}
catch(异常类型异常对象名)//捕获并处理异常
{
异常处理代码块;
}
当try描述的代码段遇到异常发生时,会抛出一个异常对象,该异常由相应的catch语句捕获并处理。
与一个try相关的catch语句可以有多个,构成多重catch语句,异常类型决定了要执行哪个catch语句。
也就是说,如果由一个catch语句指定的异常类型与发生的异常类型相符,那么就会执行这个catch语句(其他的catch语句则被跳过)。
如果catch语句执行完毕或者没有抛出异常,就会执行最后一个catch后面的第一个语句。
【例6_2】使用异常处理的例子。
publicclassExam6_2{
publicstaticvoidmain(String[]args){
try{
device(Integer.parseInt(args[0]));
}catch(ArithmeticExceptione){
System.out.println("输入为零,产生异常,被catch捕获到");
}
System.out.println("输入不为零,没有产生异常!
");
}
staticintdevice(inti){
return10/i;
}
}
程序中用户输入的值与10做除法,当用户输入的数值不为零时,程序不会抛出异常,执行结果如图6-2所示,当用户输入零时,程序抛出异常,并被catch语句捕获后处理,程序执行结果如图6-3所示。
图6-2例6_2输入不为0时程序运行结果
图6-3例6_2输入为0时程序运行结果
2.使用多重catch语句
与一个try相关的catch语句可以有多个,每一个catch语句捕获一个不同类型的异常。
当异常发生时,每一个catch子句被依次检查,第一个匹配异常类型的子句被执行。
一个catch语句执行以后,其他的子句被忽略,程序从try/catch块后的代码开始继续执行。
其形式如下所示:
try
{
可能发生异常的代码块;
}
catch(异常类型1异常对象名1)
{
异常处理代码块1;
}
…
catch(异常类型n异常对象名n)
{
异常处理代码块n;
}
【例6_3】捕获两种不同类型的异常。
publicclassExam6_3
{
publicstaticvoidmain(Stringargs[])
{
try
{
inti=args.length;
System.out.println("i="+i);
intj=5/i;
intk[]={1,2,3};
k[5]=0;
}
catch(ArithmeticExceptione)
{
System.out.println("被零除:
"+e);
}
catch(ArrayIndexOutOfBoundsExceptione)
{
System.out.println("Arrayindexoutofboundexception:
"+e);
}
System.out.println("执行catch块后的语句块");
}
}
当程序执行不输入任何命令行参数时,数组args[]的长度为空,此时i为0,运行结果如图6-4所示。
图6-4例6_3程序执行不输入命令行参数
输入命令行参数后,数据的长度大于0,i的值不为0。
程序运行结果如图6-5所示。
图6-5例6_3程序输入命令行参数后结果
两次运行的不同输出结果证实,每一个语句只对自己的异常类型做反应。
总之,catch表达式按照在程序中的顺序被检查。
只执行匹配的语句,忽略其他所有catch代码块。
3.finally关键字的使用
有时需要在catch处理异常后进行一些其他的操作,比如关闭一个已经打开了的文件,此时,可以在try/catch代码块的后面加上finally代码段来处理这种情况。
try/catch/finally的基本形式如下所示:
try
{
可能发生异常的代码块;
}
catch(异常类型异常对象名)
{
异常处理代码块;
}
…
finally
{
无论是否抛出异常都要执行的代码;
}
【例6_4】使用finally的示例。
publicclassExam6_4{
publicstaticvoidmain(String[]args){
try{
device(Integer.parseInt(args[0]));
}catch(ArithmeticExceptione){
System.out.println("输入为零,产生异常,被catch捕获到");
}
finally{
System.out.println("不论有没有产生异常,都会被执行的代码段!
");
}
}
staticintdevice(inti){
return10/i;
}
}
当命令行参数输入为0即产生异常时,该程序运行结果如图6-6所示,当命令行参数输入不为0即无异常时,程序运行结果如图6-7所示。
图6-6例6_4程序产生异常时运行结果
图6-7例6_4程序无异常时运行结果
一般地来说,finally程序块中的代码完成一些资源释放、清理的工作。
如,关闭try程序块中所有打开的文件,断开网络连接,使用getMessage()方法返回保存在某个异常中的描述字符串,使用PrintStackTrace()方法把调用堆栈的内容打印出来,等等。
6.2.2throws
有些时候不方便立即对出现的异常进行处理,Java提供了另一种处理异常的方式,将出现的异常向调用它的上一层方法抛出,由上层方法进行异常处理或继续向上一层方法抛出该异常。
在这种情况下,可以使用throws子句标记方法的声明,表明该方法不对抛出的异常进行处理,而是向调用它的方法抛出该异常。
thorws语句的使用格式如下:
[修饰符]返回类型方法名(参数1,参数2,……)throws异常列表
{……}
例如:
publicintread()throwsException
{……}
publicstaticvoidmain(Stringargs[])throwsIOException,IndexOutOfBoundsException
{……}
【例6_5】throws使用示例。
publicclassExam6_5{
publicstaticvoidmain(String[]args){
try{
device();
}catch(ArithmeticExceptione){
System.out.println("main()方法捕获到device()方法产生的异常并进行处理");
}
}
staticintdevice()throwsArithmeticException{
return10/0;
}
}
程序中的device方法中使用零做除数,会抛出异常。
但方法本身,并没有对该异常进行处理,而是抛给了调用它的main()方法进行处理,程序执行结果如图6-8所示。
图6-8例6_5程序运行结果
6.2.3抛出异常(throw)
前面的例子中所涉及的异常都是由Java虚拟机(JVM)自动产生的,如果想在某些语句中手动抛出异常对象,则可以是通过throw语句实现的,但其基本形式如下:
thrownew异常名();
【例6_6】使用throw关键字手动抛出IOException异常。
publicclassExam6_6{
publicstaticvoidmain(String[]args){
try{
device(Integer.parseInt(args[0]));
}catch(ArithmeticExceptione){
e.printStackTrace();
}
}
staticvoiddevice(inti)throwsArithmeticException{
if(i==0)thrownewArithmeticException("输入为零,请重新输入!
");
else
System.out.println("10/"+i+"="+10/i);
}
}
程序中若输入非零值,则输出10与该值的除法结果,如图6-9所示。
若输入零值,则由throw语句抛出异常,并在main()方法中捕获,使用printStackTrace()方法输出错误信息,如图6-10所示。
图6-9例6_6程序无异常时运行结果
图6-10例6_6程序产生异常时运行结果
6.2.4自定义Java异常
尽管Java的内置异常能够处理大多数常见错误,但有时还可能出现系统没有考虑到的异常,此时我们可以自己建立异常类型,来处理所遇到的特殊情况。
只要定义Exception的一个子类就可以建立自己的异常类型。
自定义异常的基本形式如下所示:
class自定义异常extends父异常类名
{
类体;
}
【例6_7】自定义异常示例。
publicclassExam6_7extendsException{
Exam6_7(){
System.out.println("输入为零,请重新输入!
");
}
}
classTest{
publicstaticvoidmain(String[]args){
try{if(args[0].equals("0"))
{
thrownewExam6_7();
}
else
{
System.out.println("10/"+args[0]+"="+10/Integer.parseInt(args[0]));
}}
catch(Exceptione){
}
}
}
程序首先创建了Exception的子类Ecam6_7,即创建了一个异常对象,然后在Test类中抛出了该异常对象,程序运行结果如图6-11所示。
图6-11例6-7产生异常时程序运行结果
6.3案例—异常实例
在本节中,我们编写一个程序对用户输入的电话号码格式进行判断,并对可能出现的异常进行抛出、使用自定义的异常进行处理。
电话号码只能是数字,且要有一定的位数。
为简单起见,我们不考虑区号,则0不能打头。
用户的输入可能正确,也可能错误,甚至忘记输入。
这一切都可以通过异常来判断,且将这些进行自定义异常会有更有好的用户界面。
我们可以通过下面的程序实现上述要求。
classTelephoneException{
staticStringtelephone;
publicstaticvoidmain(String[]args){
try{
telephone=args[0];
if(istelephone(telephone)){
System.out.println("电话号码格式输入正确!
");
}
else{
System.out.println("电话号码格式输入不正确!
");
}
}
catch(ArrayIndexOutOfBoundsExceptione){
System.out.println("没有输入电话号码!
");
}
catch(MyExceptione){
System.out.println(e.getMessage());
}
}
publicstaticbooleanistelephone(Stringtelephone)throwsMyException{
booleanistelephone=true;
if(telephone.length()!
=8){
thrownewMyException("电话号码位数不对!
");
}
try{Integer.parseInt(telephone);}
catch(NumberFormatExceptione){
thrownewMyException("不能输入数字以外的符号!
");
}
if(telephone.charAt(0)=='0'){
thrownewMyException("0不能打头!
");
}
returnistelephone;
}
}
classMyExceptionextendsException{
MyException(Stringmessage){
super(message);
}
}
在这个程序中,我们主要是通过方法istelephone()来判断用户的输入是否是正确的电话号码格式,如果用户输入格式正确,则该方法返回boolean值true,否则返回false。
在方法istelephone()中,我们通过length()方法来判断电话号码的位数是否为8位,通过将其转换成整形值来判断是否只输入了数字,通过获得首字符来判断是否首字符为0,出现上面任何一种情况,我们都在程序中抛出自定义的异常类MyException类的对象。
而在主程序中我们通过判断命令行参数的长度,来确定用户是否输入了数据。
程序的运行结果如图6-12到6-15所示。
图6-12程序TelephoneException输入位数错误时运行结果
图6-13程序TelephoneException输入符号错误时运行结果
图6-14程序TelephoneException首位错误时运行结果
图6-15程序TelephoneException输入正确时运行结果
小 结
Java中异常类具有层次组织。
其中Throwable类是Error类和Exception类的父类,是Object的直接子类。
异常类(java.lang.Exception)继承于java.lang.Object中的java.lang.Throwable类。
Java语言的异常处理机制是由捕获异常和处理异常两部分组成的。
捕获异常和处理异常可以格式化地的表示为:
try{
可能发生异常的代码块;
}
catch(异常类型1异常对象名1){
异常处理代码块1;
}
…
catch(异常类型n异常对象名n){
异常处理代码块n;
}
finally{
无论是否抛出异常都要执行的代码;
}
异常处理模块可以嵌套,可以使用throws和throw关键字主动抛出异常,其基本格式为:
[修饰符]返回类型方法名(参数1,参数2,……)throws自定义异常
{
thrownew(异常类);
}
也可以创建自定义异常类,其格式为:
class自定义异常extends父异常类名
{类体;}
实训6
一、实训目的
1.熟悉异常处理方法。
2.熟悉常见异常的捕获方法。
3.了解自定义异常。
二、实训内容
通过继承Exception类来实现自己的异常类。
并使用try-catch来捕获这个异常。
习题6
一.填空题
1.Java语言的异常处理机制是由___________和___________两部分组成的。
2.所有的异常类都是从一个名为__________的类派生出来的。
因此,当程序中发生一个异常时,就会生成一个异常类的某种类型的__________。
3.ava语言提供的异常处理机制,由___________和___________两部分组成。
4.Java程序在进行异常处理时把可能会发生异常的程序代码放在__________区块中,那么当程序执行发生异常时,__________区块会捕捉这个异常,并且以区块内的程序代码来处理异常,而__________区块则负责处理一些必要的工作。
5.Java提供了另一种处理异常的方式,将出现的异常向调用它的上一层方法抛出,由上层方法进行异常处理或继续向上一层方法抛出该异常。
在这种情况下,可以使用__________子句标记方法的声明,表明该方法不对抛出的异常进行处理,而是向__________抛出该异常。
二.判断题
1.( )Java中,所有的异常都由类来表示。
2.( )Exception类表示程序运行时发生的严重错误,Error类表示程序运行时产生的异常。
3.( )生成的异常对象将传递给Java运行时系统,这一异常的产生和提交过程称为抛弃(throw)异常。
4.( )异常处理的核心是try和catch,这两个关键字可以分开使用。
5.( )当try描述的代码段遇到异常发生时,会抛出一个异常对象,该异常由相应的catch语句捕获并处理。