异常处理

程序执行时经常会出现除零溢出、数组越界等运行错误,影响程序的正常执行。错误及异常是不可避免的,一个好的应用程序,在满足用户要求的各种功能的同时,还应具备能预见程序执行过程中可能产生的各种异常的能力,并能为异常情况给予恰当处理。在Java语言中,这种技术就是异常处理
Java语言通过面向对象的异常处理机制来解决运行期间的错误,可以预防错误的程序代码或系统错误所造成的不可预期的结果发生。减少编程人员的工作,增加了程序的灵活性,增加程序的可读性和健壮性.

传统的处理异常的办法是,函数返回一个特殊的结果来表示出现异常(通常这个特殊结果是大家约定俗称的),调用该函数的程序负责检查并分析函数返回的结果。这样做有如下的弊端:例如函数返回-1代表出现异常,但是如果函数确实要返回-1这个正确的值时就会出现混淆;可读性降低,将程序代码与处理异常的代码混爹在一起

异常处理的流程:遇到错误,方法立即结束,并不返回一个值;同时,抛出一个异常对象.调用该方法的程序也不会继续执行下去,而是搜索一个可以处理该异常的异常处理器,并执行其中的代码.
格式:try{...}//被监视的代码段,一旦发生异常,则交由其后的catch代码段处理
catch(异常类型){...}//要处理的第一种异常
catch(异常类型){...}//要处理的第二种异常
...
finally{...}//最终处理

异常的分类:基类为Throwable,Error和Exception继承Throwable,RuntimeException和IOException等继承Exception,具体的RuntimeException继承RuntimeException。

Error体系
Error类体系描述了Java运行系统中的内部错误以及资源耗尽的情形。应用程序不应该抛出这种类型的对象(一般是由虚拟机抛出)。如果出现这种错误,除了尽力使程序安全退出外,在其他方面是无能为力的。常见:VirtualMachineError虚拟机错误:OutOfMemoryException内存溢出错误,StackOverFlowError栈溢出错误。LinkageError链接错误。NoClassDefNotFoundError类定义未找到错误。Java.awt.AWTError图形界面错误


Exception体系
Exception体系包括RuntimeException体系和其他非RuntimeException的体系
RuntimeException
RuntimeException体系包括错误的类型转换ClassCastException、数组越界访问ArrayStoreException、试图访问空指针NullPointerException、算数异常ArithmeticException、违背安全原则异常SecurityException等等。
处理RuntimeException的原则是:如果出现RuntimeException,那么一定是程序员的错误。例如,可以通过检查数组下标和数组边界来避免数组越界访问异常。
其他(IOException等等)(EmptyStackException,NoSuchFieldException,classNotFoundException,CloneNotSupportedException,IllegalAccessException,InstantiationException,interruptedException)
这类异常一般是外部错误,例如试图从文件尾后读取数据等,这并不是程序本身的错误,而是在应用环境中出现的外部错误。

抛出异常
语法:throws
<throw><new><异常对象名>
为什么要声明方法抛出异常?
方法是否抛出异常与方法返回值的类型一样重要。假设方法抛出异常确没有声明该方法将抛出异常,那么客户程序员可以调用这个方法而且不用编写处理异常的代码。那么,一旦出现异常,那么这个异常就没有合适的异常控制器来解决。

捕获异常
如果一个异常没有被处理,那么,对于一个非图形界面的程序而言,该程序会被中止并输出异常信息;对于一个图形界面程序,也会输出异常的信息,但是程序并不中止,而是返回
格式:try{...}//被监视的代码段,一旦发生异常,则交由其后的catch代码段处理
catch(异常类型){...}//要处理的第一种异常
catch(异常类型){...}//要处理的第二种异常
...
finally{...}//最终处理
控制器模块必须紧接在try块后面。若掷出一个异常,异常控制机制会搜寻参数与异常类型相符的第一个控制器随后它会进入那个catch 从句,并认为异常已得到控制。一旦catch 从句结束对控制器的搜索也会停止。

再次抛出异常?
在本级中,只能处理一部分内容,有些处理需要在更高一级的环境中完成,所以应该再次抛出异常。这样可以使每级的异常处理器处理它能够处理的异常。


一些例子:
不 捕 捉“ 异 常”
“ 异 常” 对 象 是Java在 运 行 时 对 某 ?copy;“ 异 常” 情 况 作出 反 应 而 产 生 的。 例 如, 下 面 这 个 小 程 序 包 含 一 个 整 数 被0除的“ 异 常”。

class Exc0 { public static void main(String args[]) { int d = 0; int a = 42/d; } }

当Java执 行 这 个 除 法 时, 由 于 分 母 是0, 就 会 构 造 一 个“ 异常” 对 象 来 使 程 序 停 下 来 并 处 理 这 个 错 误 情 况, 在 运 行 时“ 抛 出”(throw?copy; 这 个“ 异 常”。 说“ 抛 出” 是 因 为 它 象 一个 滚 烫 的 马 铃 薯, 你 必 须 把 它 抓 住 并 立 即 处 理。 程 序 流 将会 在 除 号 操 作 符 处 被 打 断, 然 后 检 查 当 前 的 调 用 堆 栈 来查 找“ 异 常”。 一 个“ 异 常” 处 理 器 是 用 来 立 即 处 理“ 异 常” 情 况 的。 在 这 个 例 子 里, 我 们 没 有 编 一 个“ 异 常” 处 理 器,所 以 缺 省 的 处 理 器 就 发 挥 作 用 了。 缺 省 的 处 理 器 打 印Exception的字 符 ?reg; 值 和 发 生 “ 异 常” 的 地 点。 下 面 是 我 们 的 小 例子 的 输 出。

C:\>java Exc0 java.lang.arithmeticException: / by zero at Exc0.main(Exc0.java:4)

try与catch
通 常 我 们 希 望 自 己 来 处 理“ 异 常” 并 继 续 运 行。 可 以 用try来指 定 一 块 预 防 所 有“ 异 常” 的 的 程 序。 紧 跟 在try程 序 后 面,应 包 含 一 个catch子 句 来 指 定 你 想 要 捕 捉 的“ 异 常” 的 类 型。例 如, 下 面 的 例 子 是 在 前 面 的 例 子 的 基础上 构 造 的, 但 它包 含 一 个try程 序 块 和 一 个catch子 句。

class exc1 { public static void main(string args[]) { try { int d = 0; int a = 42 / d; } catch (arithmeticexception e) { system.out.println("division by zero"); } } }

catch子 句 的 目 标 是 解 决“ 异 常” 情 况, 把 一 ?copy; 变 量 设到 合 理 的 状 态, 并 象 没 有 出 错 一 样 继 续 运 行。 如 果 一 个 子程 序 不 处 理 某 个“ 异 常”, 则 返 到 上 一 级 处 理, 直 到 最 外一 级。

多 个catch子 句
在 某 ?copy; 情 况 下, 同 一 段 程 序 可 能 产 生 不 止 一 种“ 异常” 情 况。 你 可 以 放 置 多 个catch子 句, 其 中 每 一 种“ 异 常” 类 型 都 将 被 检 查, 第 一 个 与 ?reg; 匹 配 的 就 会 被 执 行。 如果 一 个 类 和 其 子 类 都 有 的 话, 应 把 子 类 放 在 前 面, 否 则 将永 远 不 会 到 达 子 类。 下 面 是 一 个 有 两 个catch子 句 的 程 序 的例 子。

class MultiCatch { public static void main(String args[]) { try { int a = args.length; System.out.println("a = " + a); int b = 42/a; int c[] = {1}; c[42] = 99; } catch(ArithmeticException e) { System.out.println("div by 0: " + e); } catch(ArrayIndexOutOfBoundsException e) { system.out.println("array index oob: " + e); } } }

如 果 在 程 序 运 行 时 不 跟 参 数, 将 会 引 起 一 个0做 除 数 的“ 异 常”, 因 为a的 值 为0。 如 果 我 们 提 ?copy; 一 个 命 令 行 参 数,将 不 会 产 生 这 个“ 异 常”, 因 为a的 值 大 于0。 但 会 引 起 一 个 ArrayIndexOutOfBoundexception的“ 异 常”, 因 为 整 型 数 组c的 长 度是1, 却 给c[42]赋 值。 下 面 是 以 上 两 种 情 况 的 运 行 结 果。

C:\>java MultiCatch a = 0 div by 0: java.lang.arithmeticexception: / by zero C:\>java MutiCatch 1 a = 1 array index oob: java.lang.ArrayIndexOutOfBoundsException:42

try语 句 的 嵌 套
你 可 以 在 一 个 成 员 函 数 调 用 的 外 面 写 一 个try语 句, 在 这个 成 员 函 数 内 部, 写 另 一 个try语 句 保 护 其 他 代 码。 每 当 遇到 一 个try语 句,“ 异 常” 的 框 架 就 放 到 堆 栈 上 面, 直 到 所 有的try语 句 都 完 成。 如 果 下 一 级 的try语 句 没 有 对 某 种“ 异 常” 进 行 处 理, 堆 栈 就 会 展 开, 直 到 遇 到 有 处 理 这 种“ 异 常” 的try语 句。 下 面 是 一 个try语 句 嵌 套 的 例 子。

class MultiNest { static void procedure() { try { int c[] = { 1 }: c[42] = 99; } catch(ArrayIndexOutOfBoundsexception e) { System.out.println("array index oob: " + e); } } public static void main(String args[]) { try { int a = args.length; system.out.println("a = " + a); int b = 42/a; procedure(); } catch(arithmeticException e) { System.out.println("div by 0: " + e); } } }

成 员 函 数procedure里 有 自 己 的try/catch控 制, 所 以main不 用 去处 理 ArrayIndexOutOfBoundsException。

throw语 句
throw语 句 用 来 明 确 地 抛 出 一 个“ 异 常”。 首 先, 你 必 须 得到 一 个Throwable的 实 例 的 控 制 柄, 通 过 参 数 传 到catch子 句, 或者 用new操 作 符 来 创 建 一 个。 下 面 是throw语 句 的 通 常 形 式。

throw ThrowableInstance;

程 序 会 在throw语 句 后 立 即 终 止, 它 后 面 的 语 句 执 行 不 到,然 后 在 包 含 它 的 所 有try块 中 从 里 向 外 寻 找 含 有 与 其 匹 配的catch子 句 的try块。 下 面 是 一 个 含 有throw语 句 的 例 子。

class ThrowDemo { static void demoproc() { try { throw new NullPointerException("de3mo"); } catch(NullPointerException e) { System.out.println("caught inside demoproc"); throw e; } } public static void main(String args[]) { try { demoproc(); } catch(NullPointerException e) { system.out.println("recaught: " + e); } } }

throws语 句
throws用 来 标 明 一 个 成 员 函 数 可 能 抛 出 的 各 种“ 异 常”。对 大 多 数Exception子 类 来 说,Java 编 译 器 会 强 迫 你 声 明 在 一个 成 员 函 数 中 抛 出 的“ 异 常” 的 类 型。 如 果“ 异 常” 的 类 型是Error或 RuntimeException, 或 它 们 的 子 类, 这 个 规 则 不 起 作 用,因 为 这 ?copy; 在 程 序 的 正 常 部 分 中 是 不 期 待 出 现 的。 如 果你 想 明 确 地 抛 出 一 个RuntimeException, 你 必 须 用throws语 句 来声 明 它 的 类 型。 这 就 重 新 定 义 了 成 员 函 数 的 定 义 语 法:

type method-name(arg-list) throws exception-list { }

下 面 是 一 段 程 序, 它 抛 出 了 一 个“ 异 常”, 但 既 没 有 捕捉 它, 也 没 有 用throws来 声 明。 这 在 编 译 时 将 不 会 通 过。

class ThrowsDemo1 { static void procedure( ) [ System.out.println("inside procedure"); throw new IllegalAccessException("demo"); } public static void main(String args[]) { procedure( ); } }

为 了 让 这 个 例 子 编 译 过 去, 我 们 需 要 声 明 成 员 函 数procedure抛出 了IllegalAccessException, 并 且 在 调 用 它 的 成 员 函 数main里 捕捉 它。 下 面 是 正 确 的 例 子:

class ThrowsDemo { static void procedure( ) throws IllegalAccessException { System.out.println("inside procedure"); throw new IllegalAccessException("demo"); } public static void main(String args[]) { try { procedure( ); } catch (IllegalAccessException e) { System.out.println("caught " + e); } } }

下 面 是 输 出 结 果:

C:\>java ThrowsDemo inside procedure caught java.lang.IllegalAccessException: demo

finally
当 一 个“ 异 常” 被 抛 出 时, 程 序 的 执 行 就 不 再 是 线 性 的,跳 过 某 ?copy; 行, 甚 至 会 由 于 没 有 与 ?reg; 匹 配 的catch子 句而 过 早 地 返 回。 有 时 确 保 一 段 代 码 不 管 发 生 什 么“ 异 常” 都 被 执 行 到 是 必 要 的, 关 键 词finally就 是 用 来 标 识 这 样 一段 代 码 的。 即 使 你 没 有catch子 句,finally程 序 块 也 会 在 执 行 try程 序 块 后 的 程 序 ?reg; 前 执 行。 每 个try语 句 都 需 要 至 少一 个 与 ?reg; 相 配 的catch子 句 或finally子 句。 一 个 成 员 函 数 返回 到 调 用 它 的 成 员 函 数, 或 者 通 过 一 个 没 捕 捉 到 的“ 异 常”,或 者 通 过 一 个 明 确 的return语 句,finally子 句 总 是 恰 好 在 成 员函 数 返 回 前 执 行。 下 面 是 一 个 例 子, 它 有 几 个 成 员 函 数,每 个 成 员 函 数 用 不 同 的 途 径 退 出, 但 执 行 了finally子 句。

class FinallyDemo { static void procA( ) { try { System.out.println("inside procA"); throw new RuntimeException("demo"); } finally { System.out.println("procA's finally"); } } static void procB( ) { try { System.out.println("inside procB"); return; } finally { System.out.println("procB's finally"); } } public static void main(String args[]) { try { procA( ); } catch (Exception e); procB( ); } }

下 面 是 这 个 例 子 的 运 行 结 果:

C:\>java FinallyDemo inside procA procA's finally inside procB procB's finally
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值