面向对象的例外处理

什 么 是 例 外 、 例 外 处 理

    例 外 就 是 在 正 常 情 况 以 外 的 事 件 。 Java 之 中 的 例 外 是 指 程 序 运 行 中 发 生 的 异 常 事 件 , 这 些 异 常事 件 将 阻 止 程 序 的 正 常 运 行 。 比 如 出 现 了 数 组 越 界 、 用户 输 入 错 误 等 等 。 而 顾 名 思 义 , 例 外 处 理 就 是 对 这 些 例外 进 行 处 理 。

    所 有 的 高 级 计 算 机 编 程 语 言 里 都 有 判断 是 否 出 现 了 例 外 的 语 句 , 每 个 好 的 计 算 机 程 序 都 会 有对 例 外 进 行 处 理 的 程 序 段 。 只 不 过 在 不 同 的 计 算 机 编 程语 言 里 面 , 对 例 外 的 称 呼 不 同 , 例 如 有 的 语 言 里 所 讲 的错 误 处 理 , 其 实 就 等 同 于 Java 里 面 的 例 外 处 理 。 这 就 像诸 葛 亮 先 生 有 两 个 名 字 , 孔 明 是 他 诸 葛 亮 也 是 他 , 还 有人 称 呼 他 为 诸 葛 先 生 … … , 只 是 称 呼 不 同 罢 了 , 意 义 是一 样 的 。

抛 弃 之

    在 许 多 高 级 语 言 中 都 是 通 过 使 用 if 语句 来 判 断 是 否 出 现 了 例 外 , 并 进 行 处 理 的 。 Java 作 为 一个 完 全 基 于 面 向 对 象 的 语 言 , 例 外 处 理 也 是 采 用 面 向 对象 的 方 法 。 在 一 个 方 法 的 运 行 过 程 中 如 果 发 生 了 例 外 ,则 这 个 方 法 将 生 成 一 个 代 表 该 例 外 的 对 象 , 并 把 它 提 交给 正 在 运 行 这 个 方 法 的 系 统 。 我 们 把 这 个 过 程 称 为 抛 弃一 个 例 外 。

    就 像 抛 弃 这 个 球 一 样 , 把 它 给 抛 掉 了。 抛 弃 一 个 例 外 的 过 程 就 和 我 们 平 时 工 作 中 碰 到 了 特 殊情 况 无 法 解 决 , 需 要 写 一 份 报 告 交 把 这 个 棘 手 的 问 题 上报 上 级 主 管 部 门 一 样 。

捕 获 之

    系 统 在 运 行 的 时 候 查 找 处 理 提 交 给 它的 例 外 的 方 法 , 这 个 过 程 称 为 捕 获 一 个 例 外 。 这 就 和 法官 判 案 时 查 找 相 应 的 法 律 条 文 的 过 程 是 一 样 的 。

优 越 性

    1. 利 用 以 上 这 些 方 法 处 理 例 外 , 使 得例 外 处 理 的 程 序 代 码 与 普 通 的 程 序 代 码 不 再 混 为 一 体 ,减 少 了 编 程 序 的 工 作 量 , 同 时 也 增 加 了 程 序 可 读 性 。

    2. 利 用 面 向 对 象 的 方 法 来 处 理 例 外 ,可 以 对 各 种 不 同 的 例 外 事 件 进 行 分 类 , 然 后 再 处 理 之 ,从 而 具 有 了 良 好 的 接 口 和 层 次 性 。

    3. 利 用 类 的 层 次 性 既 可 以 区 分 不 同 的例 外 分 别 处 理 , 也 可 以 把 具 有 相 同 父 类 的 多 个 例 外 统 一处 理 , 具 有 相 当 的 灵 活 性 。

    由 以 上 几 点 可 知 , Java 的 这 种 面 向 对 象的 例 外 处 理 机 制 为 那 些 具 有 动 态 运 行 特 性 的 复 杂 程 序 提供 了 强 有 力 的 控 制 方 式 。

    以 上 这 些 优 越 性 背 下 来 就 可 以 了 , 到时 候 可 以 用 来 在 孩 儿 们 面 前 炫 耀 一 下 自 己 的 知 识 渊 博 。

与 例 外 处 理 相 关 的 类

    用 面 向 对 象 的 方 法 来 进 行 例 外 处 理 首先 必 须 建 立 类 的 层 次 。 如 图 20.1 所 示 , 类 Throwable 是 最 顶层 的 , 只 有 它 的 子 类 才 可 以 作 为 一 个 例 外 被 抛 弃 。

运 行 时 与 非 运 行 时 例 外 

    Java 有 运 行 时 例 外 和 非 运 行 时 例 外 之 分。

    所 谓 运 行 时 例 外 就 是 由 系 统 在 Java 程 序运 行 的 过 程 中 检 测 到 的 例 外 , 例 如 除 零 溢 出 ( 除 数 为 零导 致 的 错 误 ) 、 数 组 越 界 等 。 由 于 它 们 可 能 在 程 序 的 任何 位 置 发 生 , 而 且 谁 也 无 法 在 程 序 运 行 以 前 计 算 出 它 们会 发 生 多 少 次 , 所 以 Java 语 言 编 译 器 允 许 Java 程 序 不 对 它进 行 处 理 。

    除 此 之 外 的 其 他 例 外 则 被 称 为 非 运 行时 例 外 。
面向对象的例外处理
图 20.1 例 外 处 理 — — 类 的 层 次 

    用 户 自 己 定 义 的 例 外 都 是 非 运 行 时 例外 。 然 而 并 非 所 有 非 运 行 时 例 外 都 是 用 户 自 己 定 义 的 例外 。

    所 谓 用 户 自 己 定 义 的 例 外 , 就 是 你 在编 写 程 序 的 时 候 在 你 的 程 序 里 面 定 义 的 那 些 例 外 , 以 便使 得 你 的 程 序 具 有 更 高 的 可 靠 性 , 不 会 轻 易 地 出 差 错 。

    Java 编 译 器 要 求 必 须 捕 获 或 声 明 所 有 的非 运 行 时 例 外 。 对 于 用 户 自 定 义 例 外 , 这 是 十 分 显 然 的; 否 则 , 系 统 就 不 知 道 这 些 用 户 自 定 义 的 例 外 的 特 性 。知 己 知 彼 才 能 百 战 百 胜 。
简 单 的 例 外 处 理 

    运 行 时 例 外 的 处 理

    先 看 看 下 面 这 两 个 例 子 。
程 序 20.1
public class REP{
  public static void main(String args[ ]) {
    int AE = 1/0; // AE 等 于 1 除 以 0 , 将 导 致除 0 溢 出 例 外
    }
  }

程 序 20.2
public class REP{
  public static void main(String args[ ]) {
    int i = 0;
    int AE = 1/i; // AE 等 于 1 除 以 i , 而 i 等于 0 , 也 将 导 致 除 0 溢 出
    }
  } 

    在 程 序 20.1 和 程 序 20.2 中 都 出 现 了 除 数为 0 导 致 的 “ 除 0 溢 出 ” 例 外 , 这 属 于 运 行 时 例 外 。 此 时, 运 行 程 序 的 系 统 会 自 动 把 生 成 的 例 外 对 象 交 给 缺 省 的( 也 就 是 默 认 的 ) 例 外 处 理 程 序 , 在 计 算 机 屏 幕 上 显 示出 这 个 例 外 的 内 容 及 发 生 此 例 外 的 位 置 。 在 JDK 环 境 下 运行 这 两 个 例 子 程 序 。 ( 有 关 JDK 开 发 环 境 的 介 绍 请 参 见 《开 发 环 境 》 一 章 )

    程 序 20.1 的 运 行 结 果 为 :
    c:\java\exam >javac REP.java
    REP.java:3: Arithmetic exception.
        int AE = 1/0;
            ^
    1 error

    可 见 , 程 序 在 编 译 的 时 候 编 译 器 就 指出 了 错 误 。 此 程 序 不 能 通 过 编 译 。 无 法 生 成 可 以 运 行 的文 件 。

    程 序 20.2 的 运 行 结 果 是 :
    c:\java\exam>javac REP.java
    c:\java\exam>java REP
    java.lang.ArithmeticException: / by zero
        at REP.main(REP.java:4)

    程 序 20.2 虽 然 通 过 了 编 译 , 编 译 器 没 有指 出 有 错 误 , 从 而 生 成 了 可 以 运 行 的 程 序 文 件 , 但 是 该程 序 在 运 行 时 系 统 指 出 出 现 了 例 外 。

非 运 行 时 例 外

    我 们 也 是 先 看 一 个 例 子 :
程 序 20.3
class MyE extends Exception{ // 定 义 一 个 例 外 类 MyE
    private int detailA,detailB;
    MyE(int a,int b){
        detailA = a;
        detailB = b;
        }
    public String toString(){
        return "MyException : "+detailA+"<"+detailB;
        }
    }

 public class ED{ // 定 义 一 个 类 ED
static void compare(int a,int b) throws MyE {
    System.out.println("***************************");
    System.out.println("call compare("+a+","+b+")");
    if (a< font>
         throw new MyE(a,b);
    System.out.println("normal exit : "+a+"> = "+b);
    }

 static void callcompare(int a,int b){
    try{
        compare(a,b);
      } catch(MyE e){
            System.out.println("Catch "+e);
                }
        finally{
            System.out.println("return from callcompare()");
            }
    }

 public static void main (String args[ ]){
        callcompare(10,5);
        callcompare(5,5);
        callcompare(5,10);
        }
}

    程 序 20.3 抛 弃 了 自 己 的 例 外 。 上 例 对 两个 整 数 a 和 b 进 行 了 比 较 ( compare ) , 当 a ( = b 时 程 序 正常 运 行 , 当 a((b 时 程 序 抛 弃 例 外 MyException 。 
 

在 JDK 里 例 20.3 的 运 行 结 果 如 下 :
    c:\java\exam>java ED

    ***************************
    call compare(10,5)
    normal exit : 10> = 5
    return from callcompare()
    ***************************
    call compare(5,5)
    normal exit : 5> = 5
    return from callcompare()
    ***************************
    call compare(5,10)
    Catch MyException : 5<10
    return from callcompare() 

例 外 的 抛 弃 、 捕 获 和 声 明 

    除 对 运 行 时 例 外 可 以 不 做 处 理 外 , 例外 处 理 还 有 以 下 几 种 方 法 :

  1. 使 用 throws 子 句 生 命 例 外 ;
  2. 定 义 自 己 的 例 外 类 , 并 用 throw 语 句 来 抛 弃 它;
  3. 使 用 try-catch-finally 语 句 捕 获 例 外 。
抛 弃 例 外

    在 Java 语 言 中 捕 获 一 个 例 外 之 前 , 必 须有 一 段 Java 代 码 生 成 一 个 例 外 对 象 并 把 它 抛 弃 。 抛 弃 例外 的 代 码 可 以 是 你 自 己 Java 的 程 序 , JDK 中 的 某 个 类 , 或者 是 Java 运 行 时 系 统 。 这 就 像 在 蓝 球 场 上 打 球 一 样 , 你 若想 接 到 一 个 球 , 就 必 须 有 人 先 抛 出 这 个 球 , 不 管 这 人 是中 锋 , 还 是 后 卫 , 亦 或 是 对 方 球 员 。 同 理 , 如 同 所 有 人抛 球 都 必 须 用 手 一 样 , 无 论 是 你 自 己 的 程 序 还 是 运 行 时系 统 , 都 必 须 使 用 throw 语 句 来 抛 弃 例 外 。

    例 如 , 在 例 20.3 的 compare 方 法 中 就 使 用了 throw 语 句 来 抛 弃 一 个 例 外 :

     throw new MyE(a,b);
其 中 MyE 是 Exception 的 一 个 子 类 。
 
 

    class MyE extends Exception{ // class MyException }
由 throw 抛 弃 的 例 外 必 须 是 Throwable 类 或 其 子类 的 对 象 。

声 明 例 外

    在 很 多 情 况 下 , 生 成 例 外 的 方 法 并 不需 要 处 理 它 , 而 是 用 throws 子 句 来 声 明 它 , 以 向 上 传 递 。如 例 20.3 中
    static void compare(int a,int b) throws MyE {
这 样 就 在 compare 方 法 中 声 明 了 例 外 MyE 。

    一 个 方 法 中 也 可 以 同 时 声 明 多 个 例 外, 只 需 throws 在 后 列 出 所 有 要 声 明 的 例 外 即 可 。 例 如

    static void Proc ( ) throws MyE , ArithmeticException{
    … …
    }

捕 获 例 外

    捕 获 例 外 需 使 用 try-catch-finally 语 句 。

    在 例 20.3 的 callcompare 方 法 中 , try 语 句 在 {} 中 指 定 了 一 段 代 码 , 该 代 码 可 能 会 抛 弃 几 个 例 外 ; 其后 的 catch 用 于 处 理 这 些 例 外 ; finally 则 提 供 了 统 一 的 出口 。

 static void callcompare(int a,int b){
    try{
        compare(a,b);
    }
        catch(MyE e){
            System.out.println("Catch "+e);
            }
        finally{
            System.out.println("return from callcompare()");
            }
    }
 

程 序 20.4 import java.awt.*;
import java.applet.*;

class MyE extends Exception{ // class MyException
    private int detailA,detailB;
    MyE(int a,int b){
        detailA = a;
        detailB = b;
            }
    public String toString(){
        return "MyException : "+detailA+"<"+detailB;
            }
    

}

class Cons{ // 设 置 一 个 常 数 , 让 它 代 表 两 条线 之 间 的 距 离
    final int jmp = 13;
    }

public class ED extends Applet{ // class Exception Demo
    static void compare(int a,int b,Graphics g,int Ln,int col) throws MyE {
      Cons jump = new Cons();
      g.drawString("***************************",col,Ln);
      g.drawString("call compare("+a+","+b+")",col,Ln+jump.jmp);
      if (a< font>
        throw new MyE(a,b);
      g.drawString("normal exit :"+a+"> = "+b,col,Ln+2*jump.jmp);
        }

static void callcompare(int a,int b,Graphics g,int Ln,int col){
  Cons jump = new Cons();
    try{
        compare(a,b,g,Ln,col);
      }
        catch(MyE e){
        g.drawString("Catch "+e,col,Ln+2*jump.jmp);
            }
      finally{
            g.drawString("return from callcompare()",col,Ln+3*jump.jmp);
            }
    }

public void paint (Graphics g){
    Cons jump = new Cons();
    int Ln;
    int col;
    Ln = 10; col = 20;
    callcompare(10,5,g,Ln,col);
    Ln = Ln+5*jump.jmp;
    callcompare(5,5,g,Ln,col);
    Ln = Ln+5*jump.jmp;
    callcompare(5,10,g,Ln,col);
      }
    }

    程 序 20.4 是 程 序 20.3 的 Applet 形 式 , 它 的运 行 结 果 如 图 20.2 所 示 。
面向对象的例外处理
图 20.2 程 序 20.4 的 执 行 结 果

几 件 值 得 注 意 的 事 情

  1. 自 定 义 的 例 外 类 的 类 名 通 常 可 以 以 Exception 结尾 。 例 如 :
    MyException , MyArithmeticException 等 。
  2. 对 于 运 行 时 例 外 可 以 不 捕 获 、 声 明 , 而 提 交运 行 时 系 统 处 理 。
  3. 在 捕 获 或 声 明 例 外 时 , 要 选 取 合 适 类 型 与 层次 的 例 外 。
  4. 处 理 例 外 既 可 以 在 方 法 内 捕 获 并 处 理 , 也 可以 提 交 上 层 方 法 处 理 。
  5. 使 用 finally 语 句 可 为 例 外 处 理 提 供 统 一 的 出 口, 通 常 可 在 finally 语 句 中 进 行 资 源 清 除 工 作 , 如 关 闭 已打 开 的 文 件 等 。
小 结
  • Java 采 用 面 向 对 象 的 例 外 处 理 机 制 减 小 了 编 程量 , 增 加 了 灵 活 性 , 增 强 了 程 序 的 可 读 性 和 可 靠 性 , 有利 于 编 写 具 有 动 态 运 行 特 性 的 复 杂 程 序 。
  • 对 非 运 行 时 例 外 必 须 捕 获 或 声 明 。
    对 于 自 定 义 的 例 外 类 , 通 常 作 为 类 Exception 的 子 类 , 而 不 作 为 类 Error 的 子 类 。 因 为 类 Error 通 常 用 于系 统 内 严 重 的 硬 件 错 误 。
  • 抛 弃 的 例 外 必 须 是 Throwable 类 或 其 子 类 的 对 象。
  • 注 意 区 分 throw 和 throws 的 用 法 :
    throw 用 于 抛 弃 例 外 ;
    throws 用 于 声 明 例 外 。
posted @ 2010-12-13 21:16  刘竹青  阅读(148)  评论(0编辑  收藏  举报