版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
1、第七章 异常处理本章导读对于计算机程序来说,错误和异常都是不可避免的。Java提供了丰富的错误及异常情况处理措施,对各种可能出现的异常都作了充分的考虑,可以说,从没有哪一种语言象Java这样对异常考虑的这样周全。作为一门面向对象语言,Java把它对异常的处理都封装到类里。在本章中,您将学习到以下关于异常处理的内容:l Java中异常处理的方式l Java中的异常类l 如何抛出异常l 如何捕获并处理异常如果您用过C+,您会对Java的异常处理感到非常熟悉。下面我们就来介绍它。7.1出错处理在程序运行的过程中,存在着这样那样的发生错误的可能。这些错误可能是由于包含不合法的信息的文件所造成的,可能是
2、由于不稳定的网络连接造成的,或者是数组越界存取,还可能是因为妄图使用空引用来引用对象。当这些错误发生时,我们通常希望程序能够智能的处理这些错误,而不会导致系统的崩溃或数据的丢失,例如我们希望程序在错误发生之后能够做到:l 返回一个正常的状态从而可以继续执行其它的命令;或者l 允许用户保存他们的数据,然后放心的关闭程序。我们的愿望是好的,但是往往事违人愿。通常当系统检测到错误发生时,程序已经“病入膏肓”了,不能再返回到正常的状态,也无法恢复已经破坏了的数据。因此错误处理的任务就是将错误从发生地传递到能够处理它的地方。要想在程序中处理错误,就需要了解一下到底可能发生哪些错误。我们概括一下,程序中可
3、能出现以下错误:l 用户输入错误比如用户输入了错误的数据格式,这时您需要在程序中处理它,通常这种错误比较容易处理,而且不会对系统造成“灭顶之灾”。l 设备错误指硬件不能正常工作的错误,比如打印机不工作,串口找不到,甚至硬盘损坏。这种错误有些很致命。l 物理限制导致的错误比如磁盘空间满,内存不足。l 代码错误方法不能被正确的执行。这类错误是最常见的错误,比如数组越界访问,除零错误等。有些错误是能够检查出来的或者可以预见的,比如因为程序代码本身的原因造成的错误,这些错误在经过程序员的仔细检查之后,在理论上是有可能避免的。然而还有一些错误是不可预测的,比如内存不足,文件操作的错误等等。这种错误不是程
4、序本身的错误,是和程序运行的环境有关系的。在传统的非面向对象的编程语言中,错误处理的任务全在程序员身上,程序员必须考虑在程序中可能出现的种种问题,并且自行决定如何处理这些问题。这样做有两个缺点,一是编程人员的负担过重,二是出错处理不规范,每个程序员都有自己的一套处理错误的习惯,这样就不利于程序员之间的合作,而且降低了程序的可读性。在传统的错误处理中,通常采用返回一个特定错误代码值的方式来标识错误,并由它的调用者来处理错误,这样对于调用者和被调用者来说都很麻烦。如果调用者不能处理这个错误还需要向它的上一级传递,这样当在调用者中可能出现两个错误的时候,这种传递方式就会变得非常笨拙;而且调用者有的时
5、候会懒于处理这些错误而听之任之,从而种下了错误的隐患;另外,这种处理方式将程序的正常执行的代码和处理错误的代码混杂在一起,降低了程序的可读性。当然,这种方法还是有一定的作用的,在Java中,有些地方仍然沿袭了这种传统的处理方法,比如某些I/O操作,如果失败了,就会返回一个空引用null。在Java中使用异常为程序提供了一种有效的错误处理的方式,使得方法的异常中止和错误处理有了一个清晰的接口。异常处理的方式和传统的方式有所不同,它的基本处理方式是:当一个方法引发一个异常之后,可以将异常抛出,由该方法的直接或间接调用者处理这个异常。这就是我们常说的catch-throw(捕获-抛出)方式。这种处理
6、方式使得错误的处理变得规范化,程序员可以用一致的方式来处理错误。7.2异常类Java是一门面向对象的编程语言,因此,异常在Java中也是作为类的实例的形式出现的。Java中的所有的异常类都是从Throwable类派生出来的。图7-1是Java中异常的类层次图。图7-1 Java中异常类的层次图从图7-1可以看到,Throwable类有两个直接子类Error和Exception。Error类及其子类主要用来描述一些Java运行时刻系统内部的错误或资源枯竭导致的错误,普通的程序不能从这类错误中恢复。您不能抛出这种类型的错误。这类错误出现的几率是很小的。另一个异常类的分支是Exception类和它的
7、子类们。在编程中,您对错误的处理主要是对这类错误的处理。类Exception是普通程序可以从中恢复的所有标准异常的超类。Exception类又有两个分支从RuntimeException中派生出来的类和不是从RuntimeException类中派生的类,这样分类的根据是错误发生的原因。RuntimeException是程序员编写程序不正确所导致的异常;而其它的异常则是由于一些异常的情况造成的,不是程序本身的错误,如果这些异常情况没有发生,程序本身仍然是好程序。RuntimeException类的错误又包括:l 错误的强制类型转换l 数组越界访问l 空指针操作其它异常(非RuntimeExcep
8、tion类的异常)比如:l 文件指针越界l 格式不正确的URLl 试图为一个不存在的类找到一个代表它的Class类的对象RuntimeException类的异常的产生是程序员的过失,理论上,程序员经过检查和测试可以查出这类错误。7.3在方法中声明异常在第五章讲到方法的声明时候,我们提到了方法声明中的throws子句。在throws子句中,可以声明方法中可能出现的异常。这样,方法就可以告诉编译器它可能会产生哪些异常,从而要求它的使用者必须考虑对处理这些异常,这样就使异常及时得到处理,减少了程序崩溃的几率。在下列几种情况下,Java可能会抛出异常:1. 调用的方法抛出了异常。2. 检测到了错误并使
9、用throw 语句抛出异常。3. 程序代码有错误,从而导致异常,比如数组越界错误。4. Java运行时刻系统产生内部错误。当前两种异常发生时,您应该告诉使用您的方法的人,您的方法强迫Java抛出异常。因为任何抛出异常的方法都是导致程序死亡的陷阱,如果没有任何代码来处理方法抛出的异常,就会导致程序的结束。在Java中,内置了异常的处理,在方法的声明中,可以指定方法中可能产生的异常。例如:class Test.public StringgetInput() throws IOException.如果一个方法可能抛出的异常不止一个,可以在方法的throws子句中声明多个异常,这些异常使用逗号“,”隔
10、开。例如:class Animationpublic Image loadImage(String s) throws EOFException, MalformURLException.并不是所有可能发生的异常都要在方法的声明中指定,从Error类中派生出的异常和从RuntimeException类中派生的异常就不用在方法声明中指定。这两类异常属于不检查异常(unchecked exception)。Java中的另一类异常是检查异常(checked exception)。检查异常是那些在程序中您应该处理的异常,而不检查异常则是哪些您无法干预的异常(如Error类异常)或者完全在您的控制之下,
11、您完全可以避免的异常(比如数组越界错误)。而方法的声明中必须指明所有可能发生的检查异常。方法实际抛出的异常可能是throws子句中指定的异常类或其子类的实例。比如在方法的声明中指明方法可能产生IOException,但是实际上可能抛出的异常或许是EOFException类的实例,这些异常类都是IOException的子类。当子类的方法覆盖了超类的方法时,子类方法的throws子句中声明的异常不能多于超类方法中声明的异常,否则会产生编译错误。因此,如果超类的方法没有throws子句,那么子类中覆盖它的方法也不能使用throws子句指明异常。对于接口,情况相同。7.4抛出异常您可以在程序中使用th
12、row语句(注意,这里是throw而不是throws)来抛出异常。throw语句的形式为:throw某异常类的实例程序清单7-1是一个抛出异常的说明性的范例。程序清单7-1import java.io.IOException;public class ThrowExceptionpublic static void main(String args)tryString s = getInput();System.out.println(s);catch(IOException e)System.out.println(e.getMessage();static String getInput(
13、) throws IOExceptioncharbuffer = new char20;int counter = 0;boolean flag = true;while(flag)buffercounter = (char)System.in.read();if (buffercounter='n')flag = false;counter+;if (counter>=20)IOException ae = new IOException("buffer is full");throw ae;return new String(buffer);在程序
14、清单7-1中,我们声明了一个方法getInput(),用来获取从键盘输入的字符,并以字符串对象的形式返回。我们还规定,从键盘输入的字符数不能多于20个,否则就抛出一个IOException异常。由于在方法getInput()中,可能会产生IOException异常,所以在方法的声明中必须指明这个异常:static String getInput() throws IOException在方法getInput()中,可能产生异常的地方有两处,一是Java的InputStream类的read()方法。InputStream类的read()方法的方法头如下所示:public abstract int
15、 read() throws IOException但是在方法getInput()中,我们并没有捕获和处理这个异常:另一个可能产生异常的地方是方法中的while循环:while(flag)if (buffercounter='n')flag = false;counter+;if (counter>=20)IOException ae = new IOException("buffer is full");throw ae;在这个循环中,如果输入的字符数大于20,我们就抛出一个IOException异常(这里只是作为抛出异常的一个示范,实际上这并不是I
16、OException异常)。在方法main中,我们捕获这个异常,并打印出异常的信息。如果没有异常发生,就打印出我们得到的输入字符串。运行程序清单7-1,如果我们输入如下字符:Wang Haifeng将会得到如下的输出结果:Wang Haifeng而如果我们输入超过20个字符:Somebody said: There is something就会得到一个异常信息:buffer is full由上可见,抛出异常无非就是一个三步曲:1. 确定异常类2. 创建异常类的实例3. 抛出异常一旦Java抛出了异常,方法不会被返回调用者,因此您不必担心返回值的问题,也不必提供一个缺省的返回值。您除了可以抛出J
17、ava类库中已有的异常,还可以自己定义异常类,并且抛出自己定义的异常。从Exception类或其子类中派生出您自己的异常类;在程序中需要的地方创建此异常类的实例并抛出即可。如程序清单7-2所示。程序清单7-2public class ThrowMyExceptionpublic static void main(String args)trytestMyException("My own exception");catch(Exception e)System.out.println(e.getMessage();static void testMyException(St
18、ring s) throws Exception/because of something wrong, an exception is thrown out.throw new MyException(s);class MyException extends ExceptionMyException()MyException(String info)super(info);运行此程序,将打印出异常信息:My own exception这是在创建异常实例的时候从构造方法中传递的信息。从这个例子中可见,创建自己的异常类是多么简单。此外,在方法testMyException的throws子句中,声
19、明可能抛出的异常是Exception类的实例,而实际上是其子类MyException的实例,我们看到这样做是允许的。7.4 捕获异常上一节您已经学习了如何抛出异常。异常的抛出是很简单的,只要遵循教条化的三步曲就可以了。然而异常被抛出以后,应该有相应的处理它的代码,否则,如果异常没有被处理,那么程序就会被系统终止。捕获异常并处理它要比抛出异常麻烦一些。为了捕获异常,您需要使用try.catch语句。一种简单的try.catch语句的形式如下:try/正常执行的代码.catch(异常类型 e)/这种类型的异常的处理代码.在建立了try.catch块之后,如果在try块中的任何地方抛出在catch子
20、句中指定类型的异常,Java解释器将跳过异常抛出处以后的代码,而直接跳到catch子句中的异常处理代码处开始执行异常处理。如果在try块中抛出的异常没有能够捕获它的catch块,则Java将立即退出这个方法;由于在方法的声明中已经指定了方法中可能产生的异常,所以在这个方法的调用者的代码中已经有了处理这种异常的代码,这样,这种类型的异常在此方法的调用者中得到了处理。调用者可能自己处理这种异常,也可能将这个异常放给它的调用者。异常就这样逐级上溯,直到找到处理它的代码为止。如果没有任何代码来捕获并处理这个异常,Java将结束这个程序的执行。程序清单7-3是一个捕获异常的说明性的范例。程序清单7-3i
21、mport java.io.IOException;public class CatchExceptionpublic static void main(String args)testTryCatch();static void testTryCatch()trytrythrow new IOException("exception 1");/下面这条语句将产生编译错误,因为编译器发现程序永远也执行不到那里/System.out.println("Executing here?");catch(IOException e)System.out.prin
22、tln(e.getMessage();throw new IllegalAccessException("exception 2");catch(IllegalAccessException ie)System.out.println(ie.getMessage();编译并运行程序清单7-3,将产生如下的输出结果:exception 1exception 2从此程序中也可以看出,trycatch块允许嵌套。嵌套的trycatch块的处理也很简单。当有异常抛出时,Java将检查异常抛出点所在的trycatch块,看是否有能够处理它的catch块,如果有,则异常被此catch块
23、所捕获;否则,异常被转到外层的trycatch块处理。在程序清单7-3中,我们在方法testTryCatch()中捕获并处理了异常IOException和IllegalAccessException。然而,另外一种选择是在方法testTryCatch()中并不处理此异常,而将异常留给这个方法的调用者去处理,这时,您需要在方法testTryCatch()的throws子句中指明这个异常。程序清单7-4是程序清单7-3的改写,它在方法的调用者那里捕获并处理了异常。程序清单7-4import java.io.IOException;public class MethodThrowpublic sta
24、tic void main(String args)trytestTryCatch();catch(IOException ioe)System.out.println("IOException is caught.");catch(IllegalAccessException iae)System.out.println("IllegalAccessException is caught.");static void testTryCatch() throws IOException, IllegalAccessExceptionif(Math.ran
25、dom()>0.5)throw new IOException("exception 1");else throw new IllegalAccessException("exception 2");编译并运行程序清单7-4。由于我们在程序中使用了随机数作为控制变量,所以程序的输出结果是不确定的,它可能是:IOException is caught.或IllegalAccessException is caught.在此程序中,方法testTryCatch()可能产生多种异常,所以在包含它的try块中也有可能出现不止一种类型的异常;因此,与之相应,
26、该try块也可以有多个catch子句,用来捕获多种类型的异常。捕获多种异常的try.catch语句的基本格式如下:try/正常执行的代码,可能产生异常.catch(异常类1 e)/异常类1的处理代码.catch(异常类2 e)/异常类2的处理代码.catch(异常类n e)/异常类n的处理代码.在一个方法中究竟是捕获异常,还是将这个异常向上传递给此方法的调用者去处理,这取决于您是否知道如何去处理这个异常。如果您知道如何处理这个异常,您就应该在此方法内将这个异常“就地解决”;如果您不知道如何处理它,您应该将这个异常留给它的调用者去处理,此时您需要在方法throws子句中指明这个异常。但是如果一个
27、类中的某个方法覆盖了其超类中的方法,而超类中的方法的throws子句中没有指明在子类的方法中可能产生的异常的类型,那么这类异常就必须在方法内捕获并处理,而不能留给其调用者处理。然而有的时候,您可能既希望在方法的内部对某个异常做一些您所知道的处理,比如释放某些资源,同时又希望方法的调用者能够捕获到并处理这个异常。这时,您可以在异常的处理代码中使用throw语句重新抛出异常。还有一种情况,您可能也希望在捕获了一个异常之后重新抛出一个异常,那就是在您捕获一个异常之后,您希望它作为另一个异常被方法的调用者处理。比如,您可能可以处理某些运行时刻异常,这时您可以刻意的捕获这个运行时刻异常并重新抛出一个新的
28、异常供方法的调用者处理。运行时刻异常属于不检查异常,如果您在方法的throws子句中声明了运行时刻异常,那么方法的调用者可以不处理此异常。所以,如果您要强迫方法的调用者处理某个运行时刻异常,可以采用上述的迂回的方法捕获运行时刻异常、抛出一种检查异常。程序清单7-5是一个捕获异常之后重新抛出的例子。程序清单7-5public class IndirectThrowpublic static void main(String args)trytestRuntime();catch(MyArithException me)System.out.println("Handle runtime
29、 exception here.");static void testRuntime() throws MyArithExceptiontry/此处刻意产生一个除零错的异常int i,j;j = 0;i = 1/j;catch(ArithmeticException ae)/在这里捕获运行时刻异常ArithmeticException/不作任何处理,只是重新抛出一个新异常MyArithException/因为运行时刻异常不在方法声明的throws子句中声明throw new MyArithException(ae.getMessage();class MyArithException
30、 extends ExceptionMyArithException()MyArithException(String msg)super(msg);此程序的运行结果如下:Handle runtime exception here.在testRuntime()方法中,我们刻意制造了一个除零错异常,它属于ArithmeticException异常类,而ArithmeticException类是RuntimeException类的子类。在方法声明的throws子句中,我们并没有声明ArithmeticException或RuntimeException,而是声明了自己的一个异常类MyArithEx
31、ception。这样,方法testRuntime()的调用者就不得不处理这个异常。在testRutime()方法内的trycatch块中,我们捕获了除零错的异常,但是在catch块中没有对它进行任何处理,而只是重新抛出了一个MyArithException类的异常。7.5 finally子句当Java程序抛出一个异常的时候,它会停止处理这个方法中剩下的代码,而寻找捕获异常的catch子句或者直接不正常退出方法。这样,如果在方法中已经分配了一些资源,或者打开了文件,在产生异常后就不会释放资源或关闭文件。前面我们已经介绍了一种解决办法,即捕获这个异常,在处理代码中释放这些资源,然后在重新抛出异常。
32、但是这样做有两个缺点:1. 需要程序员在两个地方书写同样的代码,以保证无论程序正常结束还是由于产生异常而不正常结束都会执行这段做清除工作的代码。2. 在异常处理中写这些代码,又将正常代码和错误处理的代码混在了一起。因此,这样做会导致程序冗余而且结构不清晰。Java中考虑到了这一点,提供了更好的处理办法,那就是使用finally子句。finally子句的形式如下:try/正常执行的代码,可能产生异常.catch(异常类1 e)/异常类1的处理代码.catch(异常类2 e)/异常类2的处理代码.catch(异常类n e)/异常类n的处理代码.finally/执行清除工作的语句.无论在try块中是否产生异常,也不管产生的异常是否会被捕获,finally中的语句最终都会被执行。具体的讲,在以下情况下finally子句中的语句将会被执行:l try块中的语句没有产生异常。在这种情况下,Java首先执行try块中的所有的语句,然后执行finally子句中的代码,最后执行try.catch.finally块后面的语句。l try块中的语句产生了异常,而且此异常在方法内被捕获。在这种情况下,Java首先执行try块中的语句,直到产生异常处,然后跳过此try块中剩下的语句,执行捕获此异常的catch子句的处理代码;然后执行finally子句中的代码。如果catch子句中没有重
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 南京医科大学康达学院《专业方向综合课程设计》2023-2024学年第一学期期末试卷
- 湖南省长沙市2024年中考数学模拟考试试卷含答案
- 九江学院《服装CAD制版》2023-2024学年第一学期期末试卷
- 江苏海洋大学《生化分离工程》2023-2024学年第一学期期末试卷
- 湖南九嶷职业技术学院《越南语阅读》2023-2024学年第一学期期末试卷
- 【物理】第十二章 简单机械 单元练习+2024-2025学年人教版物理八年级下册
- 黑龙江工商学院《文化与社会发展》2023-2024学年第一学期期末试卷
- 重庆第二师范学院《机器学习与人工智能》2023-2024学年第一学期期末试卷
- 浙江海洋大学《光电信息材料与技术》2023-2024学年第一学期期末试卷
- 中国科学技术大学《公关与营销策划》2023-2024学年第一学期期末试卷
- 《职业院校与本科高校对口贯通分段培养协议书》
- GJB9001C质量管理体系要求-培训专题培训课件
- 人教版(2024)英语七年级上册单词表
- 中医养生产业现状及发展趋势分析
- 2023年浙江省温州市中考数学真题含解析
- 司库体系建设
- 居间合同范本解
- 机电传动单向数控平台-矿大-机械电子-有图
- 妇科病盆腔炎病例讨论
- 食堂油锅起火演练方案及流程
- 有余数的除法算式300题
评论
0/150
提交评论