Download presentation
Presentation is loading. Please wait.
1
第14讲 异常处理 1/
2
教学目标 理解Java异常处理机制原理 掌握捕获与处理异常方法 掌握Java自定义异常及处理特殊异常的方法
3
引例 public class Ex8_1{ public static void main (String args[]) {
int i = 0; String greetings [] = { “Hello world!”, “No, I mean it!”, “HELLO WORLD!!”}; while (i < 4) { System.out.println (greetings[i]); i++; } } } 运行结果: Hello world! No, I mean it! HELLO WORLD!! Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: 3 at Ex8_1.main(Ex8_1.java:9)
4
引例分析 从上述例题可以发现,在Java语言中,并不是所有程序都是完美的和不会发生运行时错误的,我们将程序运行时发生的错误叫做异常。下面任何一种情况都会出现异常: 想打开的文件不存在; 在访问数组时,数组的下标值超过了数组允许的范围; 整除时除数为0; 正在装载的类文件丢失。
5
异常机制的作用 Java中的每个异常类都代表了一种运行错误,每当Java程序运行过程中发生一个可识别的运行错误时,系统都会产生一个相应的该异常类的对象,即产生一个异常。一旦一个异常对象产生了,系统中就一定有相应的机制来处理它,确保不会产生死机、死循环或其他对操作系统的损害,从而保证了整个程序运行的安全性,这就是Java的异常处理机制。 在没有异常处理的语言中,必须使用if-else或switch等语句,捕捉程序中所有可能发生的错误情况。Java的异常处理机制恰好弥补这个不足,它具有易于使用、可自定义异常类、允许抛出异常且不会降低运行速度等优点。因而在设计Java程序时,充分利用Java异常处理机制,可大大提高程序的稳定性及效率。
6
异常和异常类 作为面向对象语言,异常与其他语言要素一样,是面向对象范围的一部分,是异常类的对象。Java所有的异常对象都是继承Throwable类的实例,Throwable类是类库java.lang包中的一个类,它派生了两个子类:Error类和Exception类。如图8-1所示。 Error类被认为是不能恢复的严重错误,如系统内部错误、资源耗尽错误等。由于在些种情况下,除了通知用户并试图终止程序外几乎是不能做其他任何处理的,因此,不应该抛出这种类型的错误,而是直接让程序中断。 Exception类定义可能遇到的轻微错误,分为继承RuntimeException类的异常和非继承RuntimeException类的异常。这时,可以写代码来处理异常并继续程序执行,而不是让程序中断。
7
异常和异常类 图8-1 Java异常继承关系示意图
8
异常处理 引例中,异常发生后,Java在运行时将这个异常抛出,可是抛出后没有程序代码去捕捉它,所以程序被中断。
如果添加捕捉异常的程序代码,则可针对不同的异常做妥善的处理。 try{} catch(){} finally{}
9
异常处理示例 public static void main (String args[]) { int i = 0;
String greetings [] = {“Hello world!”, “No, I mean it!”, "HELLO WORLD!!“ }; while (i < 4) { try {System.out.println (greetings[i]); //可能出现数组越界异常的代码} catch (ArrayIndexOutOfBoundsException e){ //数组越界的异常处理 System.out.println( "Re-setting Index Value"); i = -1; } finally { System.out.println(“This is always printed”); //始终在循环过程 i++; } 运行结果: Hello world! This is always printed No, I mean it! HELLO WORLD!! Re-setting Index Value
10
异常处理方法 通过上例发现,当一个方法被调用时如果发生异常,必须采取相应措施解决出现的问题,即进行异常处理。有两种方法解决以上问题。
第一,通过try-catch语句捕获异常,并调用方法处理它。 第二,被调用的方法自己不处理异常,该异常将被抛回到调用它的方法中。它是使用throws来实现的,例如,public void troublesome() throws IOException 关键字throws之后是troublesome()出现的所有异常列表,该方法可以抛回IOException到调用它的程序中。如果有多个异常被抛出,可以使用逗号分开的列表列出所有的异常类。 声明异常
11
声明异常示例 运行结果: import java.io.*; C:\java>java Ex8_3
求平方根: 4 2.0 -4 输入的数是负数,不能求平方根 import java.io.*; class Ex8_3 extends Exception{ void test(double x) throws Ex8_3{ //声明异常 if(x < 0.0) throw new Ex8_3 (); else System.out.println(Math.sqrt(x)); } public static void main(String args[]) throws IOException{ Ex8_3 me = new Ex8_3 (); try{ System.out.println("求平方根: "); BufferedReader input = new BufferedReader(new InputStreamReader(System.in)); String str = input.readLine(); me.test(Double.parseDouble(str)); }catch(Ex8_3 e){ System.out.println("输入的数是负数,不能求平方根"); } } 程序分析:在这个程序中,定义的异常类继承了Exception异常类。在test()方法首部声明了异常Ex8_3异常,并在其方法中用throw子句指定了可能抛出的异常。
12
捕获异常 通过例8.2可知异常的捕获和处理是由try、catch和finally所组成的程序块完成的,我们先探讨try-catch语句块,其格式如下: try{ 可能产生异常的代码 //try块 } catch (ExceptionType e1 ){ //要捕获的异常类型 对此异常的处理 //异常处理,可以为空 }…… catch(ExceptionType en ){ } 异常的捕获和处理流程如下: 1. 如果try块中没有代码产生异常,那么程序将跳过catch子句 2. try块中的任意代码产生一个属于catch子句所声明类的异常,程序将跳过try块中的剩余代码,并执行与所产生异常类型匹配的catch子句的异常处理代码。 3. 如果try块中的代码产生一个不属于所有catch子句所声明类的异常,那么该方法会立即退出。
13
捕获异常(续) 这种方式将所有的代码都置入一个try块内集中处理,因此大量不同的方法调用可能生成相同的异常,只需要一个控制器就可以捕获处理。当然,如果自己捕获并处理异常,就不用在方法头部用throws语句来声明异常了。 我们已经知道,当异常产生时,方法的执行流程非线性方式执行,甚至在没有匹配catch子句时,可能从方法中过早退出。但有时,无论异常发生还是被捕获,都希望有些语句必须执行。例如,首先要打开文件,接着对文件进行处理,然后关闭文件,当然不希望将关闭文件的这段代码由异常处理来进行。finally语句就提供了这种问题的解决办法
14
重新抛出异常 Graphics g = image.getGraphics(); try{ 可能产生异常的代码 //try块 }
catch(MalformedURLException e){ System.out.println("an MalformedURLException!!"); g.dispose(); //释放对象g throw e ; //重新抛出异常e 当出现异常时,try块里的剩余代码都不会被执行,那么对象g将一直被保持,直到它的finalize方法释放它(占用内存),因此当不知道如何处理此异常时,在产生此异常之前需要对本地资源进行清理,如在catch子句里释放对象g,然后抛出已经捕获的异常。这就是重新抛出异常的最常见的原因。
15
finally子句 但是这种方法比较烦琐,因为在try块的正常代码里也必须要清除本地对象,清除对象的代码将出现在两个地方,一个在try块里,一个在catch子句里。当有多个catch子句时,每个子句都要清除对象。为解决这种重复性,Java提供了finally子句。 Graphics g = image.getGraphics(); try{ 可能产生异常的代码 //try块 } catch(MalformedURLException e){ System.out.println("an MalformedURLException!!!“); finally{ g.dispose(); //释放对象g
16
finally子句(续) finally子句是必须执行的,无论程序是否捕获到异常。当程序中出现finally子句时,执行流程分四种情况:
1. 方法不抛出任何异常。程序将执行try块里的所有代码,然后执行finally子句的代码,再执行后续代码。 2. 方法抛出catch子句能够捕获的异常,而且catch子句没有重新抛出异常。此时,程序将执行try块中的代码,直到抛出异常,然后try块的剩余代码被跳过,执行匹配的catch子句代码,接着执行finally子句的代码,再执行后续代码 3. 方法抛出catch子句能够捕获的异常,而且catch子句重新抛出异常。执行过程与第2种情况相同,只是最后异常被重新抛出。 4. 方法抛出不能被catch子句捕获的异常。此时,程序先执行try块里的代码,直到抛出异常,然后跳过try块的剩余代码,执行finally子句的代码,再将异常抛出给该方法的调用者。
17
抛出异常 Java程序在运行时如果引发了一个可以识别的错误,就会产生一个与该错误相对应的异常类的对象,这个过程称作异常的抛出,实际是相应异常类的实例的抛出。根据异常类的不同,抛出异常的方式也有所不同 系统自动抛出的异常——所有的系统定义的运行错误异常 语句抛出的异常 用户自定义的异常不可能依靠系统自动抛出,必须用throw语句明确地抛出一个异常。首先,必须知道什么情况下产生了某种异常对应的错误,然后为这个异常类创建一个实例,最后用throw语句抛出。下面是throw语句的常用格式: 返回类型 方法名(参数列表)throws 要抛出的异常类名列表{ …… throw 异常实例; …… }
18
抛出异常(续) 这样定义方法后,可通知所有要调用这个方法的上层方法,准备接受和处理它在运行中可能会抛出的异常。
一般情况下,这种抛出语句应在某种条件满足下执行,例如, void MyMethod()throws MyException{ //可能在程序中抛出MyException异常 …… if(i>100) throw(new MyException()); } 如果方法中的throw语句不止一个,则应该在方法头throws后列出所有可能的异常。
19
抛出异常(续) 若某个方法MyMethod可能产生Exception1、Exception2和Exception3 三种异常,而它们又都是Super_Exception类的子类,则应在相应的方法中声明可能抛出的异常类,语句如下: void MyMethod() throws Exception1,Exception2,Exception3 { …… //可能抛出这三个异常 } 还可以只简单地声明抛出Super_Exception,下面这种方式和上面的是等价的。 void MyMethod() throws Super_Exception{ ……//可能抛出这三个异常的父类
20
抛出异常(续) 在Java语言中如果调用了一个可能产生异常的方法,如果调用方法不处理这个异常,则在调用方法中要对这个异常类进行声明,如下面代码: void YourMethod() throws Super_Exception{ …… MyMethod();//调用了可能会抛出异常的方法 } Java语言要求所有用throws关键字声明的类和用throw抛出的对象必须是Throwable类或其子类。但如果试图抛出一个不可抛出的异常,Java编译器将会报错。
21
抛出异常-重新抛出异常 重新抛出异常 前面说过,被调用方法自己可以不处理异常,只是在方法头部简单声明(声明异常),由调用方法决定如何处理异常,或者也可以在被调用方法中捕获异常并进行处理。那么该不该捕获异常,有何规则?一般来说,捕获知道如何处理的异常,对于不知道如何处理的异常则进行声明,告诉调用方法,一个异常可能会被抛出。需要进行下一步处理的异常采用重新抛出异常的方法。
22
重新抛出异常示例 // 一个重新抛出异常的示例 public class Ex8_4 {
public static void found() throws Exception{ System.out.println("the original exception in found()"); throw new Exception("thrown from found()"); } public static void main(String args[]) throws Exception{ try{ found(); } catch(Exception e){ System.out.println(“catch in main()"); throw e; } } 运行结果: the original exception in found() catch in main() Exception in thread "mian" thrown from found() at Ex8_3.found(Ex8_3.java:4) at Ex8_3.main(Ex8_3.java:7)
23
自定义异常 为了适应各种异常,Java语言可以通过继承的方式编写自己的异常类。因为所有的异常类均继承自Exception类,所以自定义类也必须继承这个类。自定义异常类的语法如下, class 异常类名 extends Exception { 类体 } 在自定义异常类里通过编写新的方法来处理相关的异常,甚至可以不编写任何语句也可正常工作,因为Exception类已提供相当丰富的方法。
24
自定义异常示例 程序分析:异常类和普通类一样,可以有成员变量、方法,能对变量进行操作。同系统异常一样,可使用throw语句来抛出自定义异常。
// 创建自己的异常类。 public class Ex8_5 extends Exception { private String reason; private int port; public Ex8_5 (String reason,int port){ this.reason = reason; this.port = port; } public String getReason() { return reason; public int getPort() { return port; } 程序分析:异常类和普通类一样,可以有成员变量、方法,能对变量进行操作。同系统异常一样,可使用throw语句来抛出自定义异常。
25
自定义异常类及其使用示例 class MyException extends Exception{
public MyException(){ } public MyException(String message){ super(message); } } public class Ex8_6{ public static void f() throws MyException{ System.out.println("Throwing MyException"); throw new MyException("the second constructor! "); } public static void main(String args[]){ try{ f(); } catch(MyException e) { e.printStackTrace(); } } } 运行结果: Throwing MyException MyException:the second construtor! at Ex8_6.f(Ex8_6.java:7) at Ex8_5.main(Ex8_5.java:12) 程序分析:该例题通过继承异常类Exception创建了自己的异常类MyException,并增加了两个构造方法,第一个没有明确指明调用父类构造方法,但是编译器会自动调用父类默认构造方法,第二个通过使用super关键字,调用了参数为String类型的父类构造方法。PrintStackTrace()方法将打印出“the second constructor!”信息和异常发生地点的方法调用的顺序。
26
多catch语句使用示例 public class Ex8_6 { public static void test(int i) {
try { int x = i; if (x>0) throw new ArithmeticException ("this is a Arithmetic Exception!"); else if (x<0) throw new NullPointerException ("this is a NullPointer Exception!"); else throw new Exception("this is a Exception!"); } catch(ArithmeticException e){ System.out.println(e.toString()); }
27
多catch语句使用示例(续) catch(NullPointerException e) {
System.out.println(e.toString()); } catch(Exception e) { } public static void main(String[] args) { test(-1); test(0); test(1); } } 运行结果: java.lang.NullPointerException: this is a NullPointer Exception! java.lang.Exception: this is a Exception! java.lang.ArithmeticException: this is a Arithmetic Exception! 程序分析:在一个try区中存在的异常可能有多种类型,这时需要用多个catch块来捕获和处理这些异常。程序的main()方法中,给出了三个测试值,分别会有三个异常产生,每个异常发生时,,Java将依次逐个检查这些catch语句,发现与抛出的异常类型匹配时就执行那一段处理代码,而其余的不会被执行。
28
多catch语句使用注意事项 为了防止可能遗漏某一类异常catch语句,可以在后面放置一个捕获Exception类的catch语句。Exception是可以从任何类方法中“抛”出的基本类型。,因为它能够截获任何异常,从而使后面具体的异常catch语句不起作用,所以需放在最后一个位置。如果将捕获Exception的catch放在前面,编译就通不过。
29
本章小结 异常处理是成功设计一个Java程序的保证。本章介绍了Java语言中异常和异常类的概念,讨论了异常处理的方法。
异常是程序运行时出现的非正常情况,可以是由于程序设计本身的错误,也有可能是由于运行中出现了不可解决的问题造成了异常。异常往往会导致程序运行失败或者程序运行的终止。 在Java语言中提供了一系列的异常处理方法,程序中出现的异常都是以某个异常类的实例(对象)形式存在。
30
本章小结 异常类都是Exception类的子类,分为两种,一种是在Java类库中已经定义好的,叫系统定义的运行异常;另一种是由用户自己根据所设计软件的具体情况定义的异常,它也是Exception类或其子类,这一类异常在错误产生时无法自动地抛出,而是要用户设计代码创建对应的对象,并手动地用throw语句抛出。当然,凡是用户定义的异常,还要在可能产生这些异常的地方用throws语句声明这些异常类。 在Java的异常处理机制中,重点是异常的捕获和处理。Java语言用try-catch-finally语句块来捕获和处理异常,当抛出多个异常时,需要多个catch块来捕获和处理。此时,应注意catch块的捕获规则,防止捕获异常的失败。
31
本章小结 在Java中可以通过继承Exception类创建自定义异常类,一旦创建了自定义异常类,则该异常类的使用方法等同于系统异常类。
32
作业 推荐书籍: 《Robust Java中文版:Java异常处理、测试与调试》
Similar presentations