1、1. 课程名称:第六讲 异常处理2. 知识点2.1 课程说明这部分内容是 Java 程序设计语言的中的重点,在 Java 程序设计、JavaEE 开发中异常处理都是不可缺少的部分。为了是构建健壮、可维护的软件需要又大量防御性代码的存在,这些代码不是用来完成主要的业务逻辑,而是用来处理软件系统中可能出现的形形色色的错误而编写的。2.2 本次讲解知识点1、概述2、异常处理机制3、Java 异常类4、用户定义异常5、异常处理规则3. 具体内容3.1 概述3.1.1 Java 异常处理机制的优点异常处理已经成为衡量一门语言是否成熟的标准之一,目前主流的编程语言如C+、C# 、Ruby 等,大都提供了异
2、常处理机制。对于构造大型、健壮、可维护的应用而言,错误处理是整个应用需要考虑的重要方面。以前的错误处理方法,穷举所有错误情况,例如:if (用户输入包含出逗号之外的其他非数字字符)alert 坐标只能是数值goto retryelse if (用户输入不包含逗号)alert 坐标要用逗号分隔goto retryelse 业务实现代码 无法穷举所有的异常错误,异常的情况往往要多于考虑到的情况,总有“漏网之鱼”存在,程序不够健壮。 错误处理代码和业务实现代码混杂,严重影响代码的可读性,增加代码的维护难度。手工构造防御性代码:package code.javaoop.exception;/* Cre
3、ated by IntelliJ IDEA.* User: wuyoubf* Date: 11-11-13* Time: 上午 8:58* To change this template use File | Settings | File Templates.*/public class Car public static final int OK = 1;public static final int WRONG = 2;public int run() if (carTrouble() return OK; else return WRONG;private boolean carTro
4、uble() return false;package code.javaoop.exception;/* Created by IntelliJ IDEA.* User: wuyoubf* Date: 11-11-13* Time: 上午 9:06* To change this template use File | Settings | File Templates.*/public class Worker private Car car;public static final int IN_TIME = 1;public static final int LATE = 2;publi
5、c Worker(Car car) this.car = car;public int gotWork() if (car.run() = Car.OK) return IN_TIME; else walk();return LATE;public void walk() Java 异常处理的优点: 把各种不同类型的异常进行分类,用 Java 类来表示异常情况,这种类被称为 异常类。 异常流程的代码和正常流程的代码分离,提高程序的可读性。 可以灵活处理异常,如果当前方法有能力处理异常,就捕获并处理它,否则只需抛出异常。package code.javaoop.exception;/* Crea
6、ted by IntelliJ IDEA.* User: wuyoubf* Date: 11-11-13* Time: 上午 9:25* To change this template use File | Settings | File Templates.*/public class CarWrongException extends Exception public CarWrongException() public CarWrongException(String msg) super(msg);package code.javaoop.exception;import java.u
7、til.Date;/* Created by IntelliJ IDEA.* User: wuyoubf* Date: 11-11-13* Time: 上午 9:31* To change this template use File | Settings | File Templates.*/public class LateException extends Exception private Date arriveTime;private String reason;public LateException(Date arriveTime, String reason) this.arr
8、iveTime = arriveTime;this.reason = reason;public Date getArriveTime() return arriveTime;public String getReason() return reason;package code.javaoop.exception;/* Created by IntelliJ IDEA.* User: wuyoubf* Date: 11-11-13* Time: 上午 9:33* To change this template use File | Settings | File Templates.*/pu
9、blic class CarTwo public void run() throws CarWrongException if (carNotBrake() throw new CarWrongException(“Car do not brake“);if (carNotStartup() throw new CarWrongException(“Car do not start up“);private boolean carNotStartup() return false;private boolean carNotBrake() return false;package code.j
10、avaoop.exception;import net.mindview.util.RandomGenerator;import java.util.Date;/* Created by IntelliJ IDEA.* User: wuyoubf* Date: 11-11-13* Time: 上午 9:40* To change this template use File | Settings | File Templates.*/public class WorkerTwo private CarTwo car;private WorkerTwo(CarTwo car) this.car
11、= car;public void gotoWork() throws LateException try car.run(); catch (CarWrongException e) walk();Date date = new Date(System.currentTimeMillis();String reason = e.getMessage();throw new LateException(date, reason);public void walk() 3.1.2 Java 虚拟机的方法调用栈Java 虚拟机用方法调用栈(method invocation stack)来跟踪每个
12、线程中一系列的方法调用过程。该堆栈保存了每个调用方法的本地信息(比如方法的局部变量) 。每个线程都有一个独立的方法调用栈。对于 Java 应用程序的主线程,堆栈底部是程序的入口方法 main()。当一个新方法被调用是,Java 虚拟机把描述该方法的栈结构置入栈顶,位于栈顶的方法为正在执行的方法。如果方法中代码块可能抛出异常,有如下两种处理办法:(1) 在当前方法中通过 trycatch 语句捕获并处理异常。(2) 在方法的声明处通过 throws 语句声明抛出异常。当一个方法正常执行完毕,Java 虚拟机会从调用栈中弹出该方法的栈结构,然后继续处理前一个方法。3.2 异常处理机制3.2.1 使
13、用 trycatch 捕获异常try 业务实现代码 catch () alert 输入不合法goto retry 如果执行 try 块里的业务逻辑代码时出现异常,系统自动生成一个异常对象,该异常对象被提交给 Java 运行时环境,这个过程被称为抛出异常。 当 Java 运行时环境收到异常对象时,会寻找能处理该异常对象的 catch 块,如果找到合适的 catch 块并把该异常对象交个该 catch 块处理,这个过程成为捕获异常。如果Java 运行时环境找不到捕获异常的 catch 块,则运行时环境终止,Java 程序也将退出。 不管程序代码块是否处于 try 中,设置包括 catch 中,只要
14、执行该代码块时出现了异常,系统都会生成一个异常对象。范例:ExceptionDemo01.javaString inputStr = null;/br.readLine():每当在键盘上输入一行内容按回车,刚输入的内容将被 br 读取到。while (inputStr = br.readLine() != null) try /将用户输入的字符串以逗号(,)作为分隔符,分隔成 2 个字符串String posStrArr = inputStr.split(“,“);/将 2 个字符串转换成用户下棋的座标int xPos = Integer.parseInt(posStrArr0);int yP
15、os = Integer.parseInt(posStrArr1);/把对应的数组元素赋为“ 。if (!gb.boardxPos - 1yPos - 1.equals(“)System.out.println(“您输入的座标点已有棋子了,请重新输入 “);continue;gb.boardxPos - 1yPos - 1 = “; catch (Exception e) System.out.println(“您输入的座标不合法,请重新输入。下棋座标应以 x,y 的格式“);continue;gb.printBoard();System.out.println(“请输入您下棋的座标,应以 x
16、,y 的格式: “); 每个 catch 块都是专门用于处理该异常类及其子类的异常实例。 当 Java 运行时环境接收到异常对后,会依次判断对象是否是 catch 块后异常类或其子类的实例,如果是,Java 运行时环境将调用该 catch 块来处理该异常;否则再次拿该异常对象和下一个 catch 块里的异常类进行比较。 try 块后可以有多个 catch 块,try 块后使用多个 catch 块是为了针对不同异常类提供不同的异常处理方式。当系统发生不同的意外情况时,系统会生成不同的异常对象,Java 运行时环境就会根据异常对象所属的异常类来决定使用哪个 catch 块来处理异常。 通常情况下,
17、如果 try 块被执行一次,则 try 块后只有一个 catch 块会被执行,绝对不可能有多个 catch 块被执行。 try 块、catch 块后面的不能省略。 try 块里面声明的变量是代码块内局部变量,它只在 try 块内有效,catch 块中不能访问该变量。t r y s t a t e m e n t 1 ;s t a t e m e n t 2 ;. . . c a t c h ( E x c e p t i o n C l a s s 1 e 1 ) e x c e p t i o n h a n d l e r s t a t e m e n t 1 ; c a t c h (
18、 E x c e p t i o n C l a s s 2 e 2 ) e x c e p t i o n h a n d l e r s t a t e m e n t 2 ;出现异常 , 系统生成异常对象 e xe x i n s t a n c e o f E x c e p t i o n C l a s s 1 = = t r u ee x i n s t a n c e o f E x c e p t i o n C l a s s 2 = = t r u e进 入c a t c h后 将不 会向 下进 行3.2.2 异常类的继承体系Java 提供了丰富的异常类,这些异常类之间又
19、严格的继承关系。T h r o w a b l eE r r o r E x c e p t i o nR u n t i m e E x c e p t i o nI O E x c e p t i o nS Q L E x c e p t i o n范例:ExceptionDemo02.javapublic class ExceptionDemo02 public static void main(String args) try int a = Interger.parseInt(args0);int b = Interger.parseInt(args1);int c = a / b;
20、System.out.println(“您输入的两个数相除的结果是:“ + a / b); catch (IndexOutOfBoundsException ie) System.out.println(“数组越界:运行程序时输入的参数个数不够“); catch (NumberFormatException ne) System.out.println(“数字格式异常:程序只能接受整数参数“); catch (ArithmeticException ae) System.out.println(“算术异常“); catch (Exception e) e.printStackTrace();S
21、ystem.out.println(“未知异常“);3.2.3 访问异常信息所有异常对象都包含了如下几个常用方法: getMessage(): 返回该异常的详细描述字符串。 printStackTrace(): 将该异常的跟踪站信息输出到标准错误输出。 printStackTrace(PrintStream s): 将该异常的跟踪站信息输出到指定输出流。 getStackTrace(): 返回该异常的跟踪站信息。范例:ExceptionDemo03.javaimport java.io.*;public class ExceptionDemo03 public static void main
22、(String args) try FileInputStream fis = new FileInputStream(“a.txt“); catch(IOException ioe) System.out.println(ioe.getMessage();ioe.printStackTrace();3.2.4 使用 finally 回收资源如果 try 中打开了一些物理资源,如数据库连接、网络连接、磁盘文件等,这些物理资源必须显示回收。(Java 的垃圾回收机制不回收任何物理资源,只回收堆内存中得对象所占用的内存。)为了保证一定能够回收 try 中打开的物理资源,异常处理提供了 finall
23、y 块。(回收代码为什么一定要在 finally 中保证回收?)范例:ExceptionDemo04.javaimport java.io.*;public class ExceptionDemo04 public static void main(String args) FileInputStream fis = null;try fis = new FileInputStream(“a.txt“); catch(IOException ioe) System.out.println(ioe.getMessage();return;/System.exit(1); finally if(f
24、is != null) try fis.close(); catch(IOException ioe)ioe.printStackTrace();System.out.println(“finally is over!“);3.2.5 throws 子句:声明可能会出现的异常如果一个方法可能会出现异常,但没有能力处理这种异常,可以在方法声明处用throws 子句来声明抛出异常。一个方法可能会出现多种异常,throws 子句允许声明抛出多个异常,例如:public void method() throws SQLException, INException 3.2.6 throw 语句:抛出异常
25、throw 语句用于抛出异常。throw 语句抛出的对象必须是 java.lang.Throwable 类或者其子类的实例。以下是非法的:throw new String(“有人溺水了,救命啊!”);3.2.7 异常处理语句的语法规则异常处理语句主要涉及到 try、catch、finally、throw 、throws 关键字,要正确使用它们,必须遵守必要的语法规则。try 代码块不能脱离 catch 代码块或 finally 代码块单独存在。try 代码块后面可以有零个或多个 catch 代码块,还可以有零个或至多一个 finally 代码块。如果 catch 代码块和 finally 代码
26、块并存,finally 必须在 catch 之后。try 代码块后面可以只跟 finally 代码块。在 try 代码块中定义的变量的作用域为 try 代码块,在 catch 代码块和 finally 代码块中不能访问该变量。如:public class Example public static void main(String args) try int a = 1;new Example().methodA(a); catch (SpecialException e) a = 0; /编译错误 finally a+; /编译错误public void methodA(int val) t
27、hrows SpecialExceptionpublic class SpecialException extends Exceptionpublic class Example public static void main(String args) int a = 1;try new Example().methodA(a); catch (SpecialException e) a = 0; /编译错误 finally a+; /编译错误public void methodA(int val) throws SpecialExceptionThrow 语句后面不允许紧跟其他语句,因为这些
28、语句永远不会被执行。3.3 Java 异常类3.3.1 分类Java 的异常分为两种,Checked 异常(受检查异常)和 Runtime 异常(运行时异常)。所有 RuntimeException 类和其子类的实例被称为 Runtime 异常,也就是说,当程序中可能出现这类异常时,即使没有用 trycatch 语句捕获它,也没有用 throws 子句声明抛出它,还是会编译通过。其他非 RuntimeException 类及其子类的实例被称为 Checked 异常。这种异常的特点是 Java 编译器会检查它,也就是说,当程序中出现可能出现这类异常时,要么trycatch 语句捕获它,要么用 t
29、hrows 子句声明抛出它,否则编译不会通过。只有 Java 提供了 Checked 异常,Java 程序必须显示处理 Checked 异常,否则程序编译时会出错。两种处理方式: 程序使用 trycatch 块捕获和处理异常。 当前方法不知道如何处理,在定义该方法时声明抛出该异常。3.4 用户定义异常package code.javaoop.exception;import static net.mindview.util.Print.print;/* Created by IntelliJ IDEA.* User: wuyoubf* Date: 11-11-13* Time: 下午 1:39
30、* To change this template use File | Settings | File Templates.*/public class ServerTimedOutException extends Exceptionprivate String reason; /异常原因private int port; /服务器端口public ServerTimedOutException(String reason, int port) this.reason = reason;this.port = port;public String getReason() return re
31、ason;public int getPort() return port;public static void main(String args) try throw new ServerTimedOutException(“Could not connect!“, 80); catch(ServerTimedOutException stoe) print(stoe.getMessage();print(stoe.getStackTrace();3.4.1 异常转译和异常连异常链就是把原始异常包装为新的异常类,也就是说在新的异常类中封装了原始异常类,这有助于查找产生异常的根本原因。pack
32、age code.javaoop.exception;import java.io.*;/* Created by IntelliJ IDEA.* User: wuyoubf* Date: 11-11-13* Time: 下午 1:59* To change this template use File | Settings | File Templates.*/public class BaseException extends Exception protected Throwable cause = null;public BaseException() public BaseExcep
33、tion(String msg) super(msg);public BaseException(Throwable cause) this.cause = cause;public BaseException(String msg, Throwable cause) super(msg);this.cause = cause;public Throwable InitCause(Throwable cause) this.cause = cause;return this;Overridepublic Throwable getCause() return cause;Overridepub
34、lic void printStackTrace() printStackTrace(System.err);Overridepublic void printStackTrace(PrintStream outStream) printStackTrace(new PrintWriter(outStream);Overridepublic void printStackTrace(PrintWriter writer) super.printStackTrace(writer);if(getCause() != null) getCause().printStackTrace(writer);writer.flush();