JAVA异常处理机制_第1页
JAVA异常处理机制_第2页
JAVA异常处理机制_第3页
JAVA异常处理机制_第4页
JAVA异常处理机制_第5页
已阅读5页,还剩17页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

JAVA 异常处理机制 1 引子引子 try catch finally 恐怕是大家再熟悉不过的语句了 而且感觉用起来也是 很简单 逻辑上似乎也是很容易理解 不过 我亲自体验的 教训 告诉我 这个东西可不是想象中的那么简单 听话 不信 那你看看下面的代码 猜 猜 它执行后的结果会是什么 不要往后看答案 也不许执行代码看真正答案 哦 如果你的答案是正确 那么这篇文章你就不用浪费时间看啦 package myExample testException public class TestException public TestException boolean testEx throws Exception boolean ret true try ret testEx1 catch Exception e System out println testEx catch exception ret false throw e finally System out println testEx finally return value ret return ret boolean testEx1 throws Exception boolean ret true try ret testEx2 if ret return false System out println testEx1 at the end of try return ret catch Exception e System out println testEx1 catch exception ret false throw e finally System out println testEx1 finally return value ret return ret boolean testEx2 throws Exception boolean ret true try int b 12 int c for int i 2 i 2 i c b i System out println i i return true catch Exception e System out println testEx2 catch exception ret false throw e finally System out println testEx2 finally return value ret return ret public static void main String args TestException testException1 new TestException try testException1 testEx catch Exception e e printStackTrace 你的答案是什么 是下面的答案吗 i 2 i 1 testEx2 catch exception testEx2 finally return value false testEx1 catch exception testEx1 finally return value false testEx catch exception testEx finally return value false 如果你的答案真的如上面所说 那么你错啦 那就建议你仔细看一看 这篇文章或者拿上面的代码按各种不同的情况修改 执行 测试 你会发现有 很多事情不是原来想象中的那么简单的 现在公布正确答案 i 2 i 1 testEx2 catch exception testEx2 finally return value false testEx1 finally return value false testEx finally return value false 2 基础知识基础知识 2 1 相关概念相关概念 例外是在程序运行过程中发生的异常事件 比如除 0 溢出 数组越界 文 件找不到等 这些事件的发生将阻止程序的正常运行 为了加强程序的鲁棒性 程序设计时 必须考虑到可能发生的异常事件并做出相应的处理 C 语言中 通过使用 if 语句来判断是否出现了例外 同时 调用函数通过被调用函数的返 回值感知在被调用函数中产生的例外事件并进行处理 全程变量 ErroNo 常常用 来反映一个异常事件的类型 但是 这种错误处理机制会导致不少问题 Java 通过面向对象的方法来处理例外 在一个方法的运行过程中 如果发 生了例外 则这个方法生成代表该例外的一个对象 并把它交给运行时系统 运行时系统寻找相应的代码来处理这一例外 我们把生成例外对象并把它提交 给运行时系统的过程称为抛弃 throw 一个例外 运行时系统在方法的调用栈中 查找 从生成例外的方法开始进行回朔 直到找到包含相应例外处理的方法为 止 这一个过程称为捕获 catch 一个例外 2 2 Throwable 类及其子类类及其子类 用面向对象的方法处理例外 就必须建立类的层次 类 Throwable 位于 这一类层次的最顶层 只有它的后代才可以做为一个例外被抛弃 图 1 表示了 例外处理的类层次 从图中可以看出 类 Throwable 有两个直接子类 Error 和 Exception Error 类对象 如动态连接错误等 由 Java 虚拟机生成并抛弃 通常 Java 程序不 对这类例外进行处理 Exception 类对象是 Java 程序处理或抛弃的对象 它有 各种不同的子类分别对应于不同类型的例外 其中类 RuntimeException 代表运 行时由 Java 虚拟机生成的例外 如算术运算例外 ArithmeticException 由除 0 错 等导致 数组越界例外 ArrayIndexOutOfBoundsException 等 其它则为非运行 时例外 如输入输出例外 IOException 等 Java 编译器要求 Java 程序必须捕获 或声明所有的非运行时例外 但对运行时例外可以不做处理 图 1 例外处理的类层次 2 3 异常处理关键字异常处理关键字 Java 的异常处理是通过 5 个关键字来实现的 try catch throw throws finally JB 的在线帮助中对这几个关键字是这样解 释的 Throws Lists the exceptions a method could throw Throw Transfers control of the method to the exception handler Try Opening exception handling statement Catch Captures the exception Finally Runs its code before terminating the program 2 3 1 try 语句语句 try 语句用大括号 指定了一段代码 该段代码可能会抛弃一个或多个例外 2 3 2 catch 语句语句 catch 语句的参数类似于方法的声明 包括一个例外类型和一个例外对象 例外 类型必须为 Throwable 类的子类 它指明了 catch 语句所处理的例外类型 例外 对象则由运行时系统在 try 所指定的代码块中生成并被捕获 大括号中包含对 象的处理 其中可以调用对象的方法 catch 语句可以有多个 分别处理不同类的例外 Java 运行时系统从上到下分别 对每个 catch 语句处理的例外类型进行检测 直到找到类型相匹配的 catch 语句 为止 这里 类型匹配指 catch 所处理的例外类型与生成的例外对象的类型完 全一致或者是它的父类 因此 catch 语句的排列顺序应该是从特殊到一般 也可以用一个 catch 语句处理多个例外类型 这时它的例外类型参数应该是这 多个例外类型的父类 程序设计中要根据具体的情况来选择 catch 语句的例外 处理类型 2 3 3 finally 语句语句 try 所限定的代码中 当抛弃一个例外时 其后的代码不会被执行 通过 finally 语句可以指定一块代码 无论 try 所指定的程序块中抛弃或不抛弃例外 也无 论 catch 语句的例外类型是否与所抛弃的例外的类型一致 finally 所指定的代码 都要被执行 它提供了统一的出口 通常在 finally 语句中可以进行资源的清除 工作 如关闭打开的文件等 2 3 4 throws 语句语句 throws 总是出现在一个函数头中 用来标明该成员函数可能抛出的各种异 常 对大多数 Exception 子类来说 Java 编译器会强迫你声明在一个成员函数 中抛出的异常的类型 如果异常的类型是 Error 或 RuntimeException 或它们 的子类 这个规则不起作用 因为这在程序的正常部分中是不期待出现的 如果你想明确地抛出一个 RuntimeException 你必须用 throws 语句来声明它的 类型 2 3 5 throw 语句语句 throw 总是出现在函数体中 用来抛出一个异常 程序会在 throw 语句后立 即终止 它后面的语句执行不到 然后在包含它的所有 try 块中 可能在上层 调用函数中 从里向外寻找含有与其匹配的 catch 子句的 try 块 3 关键字及其中语句流程详解关键字及其中语句流程详解 3 1 try 的嵌套的嵌套 你可以在一个成员函数调用的外面写一个 try 语句 在这个成员函数内部 写 另一个 try 语句保护其他代码 每当遇到一个 try 语句 异常的框架就放到堆栈 上面 直到所有的 try 语句都完成 如果下一级的 try 语句没有对某种异常进行 处理 堆栈就会展开 直到遇到有处理这种异常的 try 语句 下面是一个 try 语 句嵌套的例子 class MultiNest static void procedure try int a 0 int b 42 a catch java lang ArithmeticException e System out println in procedure catch ArithmeticException e public static void main String args try procedure catch java lang Exception e System out println in main catch Exception e 这个例子执行的结果为 in procedure catch ArithmeticException java lang ArithmeticException by zero 成员函数 procedure 里有自己的 try catch 控制 所以 main 不用去处理 ArrayIndexOutOfBoundsException 当然如果如同最开始我们做测试的例子一样 在 procedure 中 catch 到异常时使用 throw e 语句将异常抛出 那么 main 当然还 是能够捕捉并处理这个 procedure 抛出来的异常 例如在 procedure 函数的 catch 中的 System out 语句后面增加 throw e 语句之后 执行结果就变为 in procedure catch ArithmeticException java lang ArithmeticException by zero in main catch Exception java lang ArithmeticException by zero 3 2 try catch 程序块的执行流程以及执行结果程序块的执行流程以及执行结果 相对于 try catch finally 程序块而言 try catch 的执行流程以及执行结果还是比 较简单的 首先执行的是 try 语句块中的语句 这时可能会有以下三种情况 1 如果 try 块中所有语句正常执行完毕 那么就不会有其他的 动做 被执行 整个 try catch 程序块正常完成 2 如果 try 语句块在执行过程中碰到异常 V 这时又分为两种情况进 行处理 如果异常 V 能够被与 try 相应的 catch 块 catch 到 那么第一个 catch 到这个异常的 catch 块 也是离 try 最近的一个与异常 V 匹配 的 catch 块 将被执行 如果 catch 块执行正常 那么 try catch 程序 块的结果就是 正常完成 如果该 catch 块由于原因 R 突然中止 那么 try catch 程序块的结果就是 由于原因 R 突然中止 completes abruptly 如果异常 V 没有 catch 块与之匹配 那么这个 try catch 程序块的结 果就是 由于抛出异常 V 而突然中止 completes abruptly 3 如果 try 由于其他原因 R 突然中止 completes abruptly 那么这 个 try catch 程序块的结果就是 由于原因 R 突然中止 completes abruptly 3 3 try catch finally 程序块的执行流程以及执行结果程序块的执行流程以及执行结果 try catch finally 程序块的执行流程以及执行结果比较复杂 首先执行的是 try 语句块中的语句 这时可能会有以下三种情况 1 如果 try 块中所有语句正常执行完毕 那么 finally 块的居于就会被 执行 这时分为以下两种情况 如果 finally 块执行顺利 那么整个 try catch finally 程序块正常完 成 如果 finally 块由于原因 R 突然中止 那么 try catch finally 程序块 的结局是 由于原因 R 突然中止 completes abruptly 2 如果 try 语句块在执行过程中碰到异常 V 这时又分为两种情况进 行处理 如果异常 V 能够被与 try 相应的 catch 块 catch 到 那么第一个 catch 到这个异常的 catch 块 也是离 try 最近的一个与异常 V 匹配 的 catch 块 将被执行 这时就会有两种执行结果 如果 catch 块执行正常 那么 finally 块将会被执行 这时分为 两种情况 如果 finally 块执行顺利 那么整个 try catch finally 程序块 正常完成 如果 finally 块由于原因 R 突然中止 那么 try catch finally 程序块的结局是 由于原因 R 突然中止 completes abruptly 如果 catch 块由于原因 R 突然中止 那么 finally 模块将被执行 分为两种情况 如果如果 finally 块执行顺利 那么整个 try catch finally 程 序块的结局是 由于原因 R 突然中止 completes abruptly 如果 finally 块由于原因 S 突然中止 那么整个 try catch finally 程序块的结局是 由于原因 S 突然中止 completes abruptly 原因 R 将被抛弃 注意 这里就正好和我们的例子相符合 虽然我们在 testEx2 中使用 throw e 抛出了异常 但是由于 testEx2 中有 finally 块 而 finally 块的执行结果是 complete abruptly 的 别小看这个用得 最多的 return 它也是一种导致 complete abruptly 的原因之一啊 后文中有关于导致 complete abruptly 的原因分析 所以整 个 try catch finally 程序块的结果是 complete abruptly 所以 在 testEx1 中调用 testEx2 时是捕捉不到 testEx1 中抛出的那个异 常的 而只能将 finally 中的 return 结果获取到 如果在你的代码中期望通过捕捉被调用的下级函数的异常来给定 返回值 那么一定要注意你所调用的下级函数中的 finally 语句 它有可能会使你 throw 出来的异常并不能真正被上级调用函数可 见的 当然这种情况是可以避免的 以 testEx2 为例 如果你一 定要使用 finally 而且又要将 catch 中 throw 的 e 在 testEx1 中被捕 获到 那么你去掉 testEx2 中的 finally 中的 return 就可以了 这个事情已经在 OMC2 0 的 MIB 中出现过啦 服务器的异常不 能完全被反馈到客户端 如果异常 V 没有 catch 块与之匹配 那么 finally 模块将被执行 分为两种情况 如果 finally 块执行顺利 那么整个 try catch finally 程序块的结 局就是 由于抛出异常 V 而突然中止 completes abruptly 如果 finally 块由于原因 S 突然中止 那么整个 try catch finally 程序块的结局是 由于原因 S 突然中止 completes abruptly 异常 V 将被抛弃 3 如果 try 由于其他原因 R 突然中止 completes abruptly 那么 finally 块被执行 分为两种情况 如果 finally 块执行顺利 那么整个 try catch finally 程序块的结局 是 由于原因 R 突然中止 completes abruptly 如果 finally 块由于原因 S 突然中止 那么整个 try catch finally 程 序块的结局是 由于原因 S 突然中止 completes abruptly 原 因 R 将被抛弃 3 4 try catch finally 程序块中的程序块中的 return 从上面的 try catch finally 程序块的执行流程以及执行结果一节中可以看出无论 try 或 catch 中发生了什么情况 finally 都是会被执行的 那么写在 try 或者 catch 中的 return 语句也就不会真正的从该函数中跳出了 它的作用在这种情况 下就变成了将控制权 语句流程 转到 finally 块中 这种情况下一定要注意返 回值的处理 例如 在 try 或者 catch 中 return false 了 而在 finally 中又 return true 那么这 种情况下不要期待你的 try 或者 catch 中的 return false 的返回值 false 被上级调 用函数获取到 上级调用函数能够获取到的只是 finally 中的返回值 因为 try 或者 catch 中的 return 语句只是转移控制权的作用 3 5 如何抛出异常如何抛出异常 如果你知道你写的某个函数有可能抛出异常 而你又不想在这个函数中对异常 进行处理 只是想把它抛出去让调用这个函数的上级调用函数进行处理 那么 有两种方式可供选择 第一种方式 直接在函数头中 throws SomeException 函数体中不需要 try catch 比如将最开始的例子中的 testEx2 改为下面的方式 那么 testEx1 就能 捕捉到 testEx2 抛出的异常了 boolean testEx2 throws Exception boolean ret true int b 12 int c for int i 2 i 2 i c b i System out println i i return true 第二种方式 使用 try catch 在 catch 中进行一定的处理之后 如果有必要的话 抛出某种异常 例如上面的 testEx2 改为下面的方式 testEx1 也能捕获到它抛 出的异常 boolean testEx2 throws Exception boolean ret true try int b 12 int c for int i 2 i 2 i c b i System out println i i return true catch Exception e System out println testEx2 catch exception Throw e 第三种方法 使用 try catch finally 在 catch 中进行一定的处理之后 如果有必 要的话 抛出某种异常 例如上面的 testEx2 改为下面的方式 testEx1 也能捕 获到它抛出的异常 boolean testEx2 throws Exception boolean ret true try int b 12 int c for int i 2 i 2 i c b i System out println i i throw new Exception aaa return true catch java lang ArithmeticException e System out println testEx2 catch exception ret false throw new Exception aaa finally System out println testEx2 finally return value ret 4 关于关于 abrupt completion 前面提到了 complete abruptly 暂且理解为 突然中止 或者 异常结束 吧 它主要包含了两种大的情形 abrupt completion of expressions and statements 下 面就分两种情况进行解释 4 1 Normal and Abrupt Completion of Evaluation 每一个表达式 expression 都有一种使得其包含的计算得以一步步进行的正常 模式 如果每一步计算都被执行且没有异常抛出 那么就称这个表达式 正常 结束 complete normally 如果这个表达式的计算抛出了异常 就称为 异 常结束 complete abruptly 异常结束通常有一个相关联的原因 associated reason 通常也就是抛出一个异常 V 与表达式 操作符相关的运行期异常有 A class instance creation expression array creation expression or string concatenation operatior expression throws an OutOfMemoryError if there is insufficient memory available An array creation expression throws a NegativeArraySizeException if the value of any dimension expression is less than zero A field access throws a NullPointerException if the value of the object reference expression is null A method invocation expression that invokes an instance method throws a NullPointerException if the target reference is null An array access throws a NullPointerException if the value of the array reference expression is null An array access throws an ArrayIndexOutOfBoundsException if the value of the array index expression is negative or greater than or equal to the length of the array A cast throws a ClassCastException if a cast is found to be impermissible at run time An integer division or integer remainder operator throws an ArithmeticException if the value of the right hand operand expression is zero An assignment to an array component of reference type throws an ArrayStoreException when the value to be assigned is not compatible with the component type of the array 4 2 Normal and Abrupt Completion of Statements 正常情况我们就不多说了 在这里主要是列出了 abrupt completion 的几种情况 break continue and return 语句将导致控制权的转换 从而使得 statements 不能正常地 完整地执行 某些表达式的计算也可能从 java 虚拟机抛出异常 这些表达式在上一 小节中已经总结过了 一个显式的的 throw 语句也将导致异常的抛出 抛出异常也是导致控制权的转换的原因 或者说是阻止 statement 正常 结束的原因 如果上述事件发生了 那么这些 statement 就有可能使得其正常情况下应该 都执行的语句不能完全被执行到 那么这些 statement 也就是被称为是 complete abruptly 导致 abrupt completion 的几种原因 A break with no label A break with a given label A continue with no label A continue with a given label A return with no value A return with a given value A throw with a given value including exceptions thrown by the Java virtual machine 5 关于我们的编程的一点建议关于我们的编程的一点建议 弄清楚 try catch finally 的执行情况后我们才能正确使用它 如果我们使用的是 try catch finally 语句块 而我们又需要保证有异常时能够抛 出异常 那么在 finally 语句中就不要使用 return 语句了 finally 语句块的最重 要的作用应该是释放申请的资源 因为 finally 中的 return 语句会导致我们的 throw e 被抛弃 在这个 try catch finally 的外面将只能看到 finally 中的返回值 除非在 finally 中抛出异常 我们需要记住 不仅 throw 语句是 abrupt completion 的原因 return break continue 等这些看起来很正常的语句也是导 致 abrupt completion 的原因 1 引言 在 JAVA 语言出现以前 传统的异常处理方式多采用返回值来标识程序出现的异常情况 这种 方式虽然为程序员所熟悉 但却有多个坏处 首先 一个 API 可以返回任意的返回值 而这些 返回值本身并不能解释该返回值是否代表一个异常情况发生了和该异常的具体情况 需要调用 API 的程序自己判断并解释返回值的含义 其次 并没有一种机制来保证异常情况一定会得到 处理 调用程序可以简单的忽略该返回值 需要调用 API 的程序员记住去检测返回值并处理异 常情况 这种方式还让程序代码变得晦涩冗长 当进行 IO 操作等容易出现异常情况的处理时 你会发现代码的很大部分用于处理异常情况的 switch 分支 程序代码的可读性变得很差 上面提到的问题 JAVA 的异常处理机制提供了很好的解决方案 通过抛出 JDK 预定义或者自 定义的异常 能够表明程序中出现了什么样的异常情况 而且 JAVA 的语言机制保证了异常一定 会得到恰当的处理 合理的使用异常处理机制 会让程序代码清晰易懂 2 JAVA 异常的处理机制 当程序中抛出一个异常后 程序从程序中导致异常的代码处跳出 java 虚拟机检测寻找和 try 关键字匹配的处理该异常的 catch 块 如果找到 将控制权交到 catch 块中的代码 然后 继续往下执行程序 try 块中发生异常的代码不会被重新执行 如果没有找到处理该异常的 catch 块 在所有的 finally 块代码被执行和当前线程的所属的 ThreadGroup 的 uncaughtException 方法被调用后 遇到异常的当前线程被中止 3 JAVA 异常的类层次 JAVA 异常的类层次如下图所示 图 1 JAVA 异常的类层次 Throwable 是所有异常的基类 程序中一般不会直接抛出 Throwable 对象 Exception 和 Error 是 Throwable 的子类 Exception 下面又有 RuntimeException 和一般的 Exception 两类 可以把 JAVA 异常分为三类 第一类是 Error Error 表示程序在运行期间出现了十分严重 不可恢复的错误 在这种 情况下应用程序只能中止运行 例如 JAVA 虚拟机出现错误 Error 是一种 unchecked Exception 编译器不会检查 Error 是否被处理 在程序中不用捕获 Error 类型的异常 一般情 况下 在程序中也不应该抛出 Error 类型的异常 第二类是 RuntimeException RuntimeException 是一种 unchecked Exception 即表示编译器不会检查程序是否对 RuntimeException 作了处理 在程序中不必捕获 RuntimException 类型的异常 也不必在方法体声明抛出 RuntimeException 类 RuntimeException 发生的时候 表示程序中出现了编程错误 所以应该找出错误修改程序 而不是去捕获 RuntimeException 第三类是一般的 checked Exception 这也是在编程中使用最多的 Exception 所有继承 自 Exception 并且不是 RuntimeException 的异常都是 checked Exception 如图 1 中的 IOException 和 ClassNotFoundException JAVA 语言规定必须对 checked Exception 作处理 编译器会对此作检查 要么在方法体中声明抛出 checked Exception 要么使用 catch 语句捕获 checked Exception 进行处理 不然不能通过编译 checked Exception 用 于以下的语义环境 1 该异常发生后是可以被恢复的 如一个 Internet 连接发生异常被中止后 可以重新连 接再进行后续操作 2 程序依赖于不可靠的外部条件 该依赖条件可能出错 如系统 IO 3 该异常发生后并不会导致程序处理错误 进行一些处理后可以继续后续操作 4 JAVA 异常处理中的注意事项 合理使用 JAVA 异常机制可以使程序健壮而清晰 但不幸的是 JAVA 异常处理机制常常被错 误的使用 下面就是一些关于 Exception 的注意事项 1 不要忽略 checked Exception 请看下面的代码 try method1 method1 抛出 ExceptionA catch ExceptionA e e printStackTrace 上面的代码似乎没有什么问题 捕获异常后将异常打印 然后继续执行 事实上在 catch 块中 对发生的异常情况并没有作任何处理 打印异常不能是算是处理异常 因为在程序交付运行后调 试信息就没有什么用处了 这样程序虽然能够继续执行 但是由于这里的操作已经发生异常 将会导致以后的操作并不能按照预期的情况发展下去 可能导致两个结果 一是由于这里的异常导致在程序中别的地方抛出一个异常 这种情况会使程序员在调试时感到 迷惑 因为新的异常抛出的地方并不是程序真正发生问题的地方 也不是发生问题的真正原因 另外一个是程序继续运行 并得出一个错误的输出结果 这种问题更加难以捕捉 因为很可能 把它当成一个正确的输出 那么应该如何处理呢 这里有四个选择 1 处理异常 进行修复以让程序继续执行 2 重新抛出异常 在对异常进行分析后发现这里不能处理它 那么重新抛出异常 让调用 者处理 3 将异常转换为用户可以理解的自定义异常再抛出 这时应该注意不要丢失原始异常信息 见 5 4 不要捕获异常 因此 当捕获一个 unchecked Exception 的时候 必须对异常进行处理 如果认为不必要在 这里作处理 就不要捕获该异常 在方法体中声明方法抛出异常 由上层调用者来处理该异常 2 不要一次捕获所有的异常 请看下面的代码 try method1 method1 抛出 ExceptionA method2 method1 抛出 ExceptionB method3 method1 抛出 ExceptionC catch Exception e 这是一个很诱人的方案 代码中使用一个 catch 子句捕获了所有异常 看上去完美而且简洁 事实上很多代码也是这样写的 但这里有两个潜在的缺陷 一是针对 try 块中抛出的每种 Exception 很可能需要不同的处理和恢复措施 而由于这里只有一个 catch 块 分别处理就 不能实现 二是 try 块中还可能抛出 RuntimeException 代码中捕获了所有可能抛出的 RuntimeException 而没有作任何处理 掩盖了编程的错误 会导致程序难以调试 下面是改正后的正确代码 try method1 method1 抛出 ExceptionA method2 method1 抛出 ExceptionB method3 method1 抛出 ExceptionC catch ExceptionA e catch ExceptionB e catch ExceptionC e 3 使用 finally 块释放资源 finally 关键字保证无论程序使用任何方式离开 try 块 finally 中的语句都会被执行 在以 下三种情况下会进入 finally 块 1 try 块中的代码正常执行完毕 2 在 try 块中抛出异常 3 在 try 块中执行 return break continue 因此 当你需要一个地方来执行在任何情况下都必须执行的代码时 就可以将这些 代码放入 finally 块中 当你的程序中使用了外界资源 如数据库连接 文件等 必须将释放这 些资源的代码写入 finally 块中 必须注意的是 在 finally 块中不能抛出异常 JAVA 异常处理机制保证无论在任何情况下必须 先执行 finally 块然后在离开 try 块 因此在 try 块中发生异常的时候 JAVA 虚拟机先转到 finally 块执行 finally 块中的代码 finally 块执行完毕后 再向外抛出异常 如果在 finally 块 中抛出异常 try 块捕捉的异常就不能抛出 外部捕捉到的异常就是 finally 块中的异常信息 而 try 块中发生的真正的异常堆栈信息则丢失了 请看下面的代码 Connection con null try con dataSource getConnection catch SQLException e throw e 进行一些处理后再将数据库异常抛出给调用者处理 finally try con close catch SQLException e e printStackTrace 运行程序后 调用者得到的信息如下 java lang NullPointerException at myPackage MyClass method1 methodl java 266 而不是我们期望得到的数据库异常 这是因为这里的 con 是 null 的关系 在 finally 语句中抛 出了 NullPointerException 在 finally 块中增加对 con 是否为 null 的判断可以避免产生这种 情况 4 异常不能影响对象的状态 异常产生后不能影响对象的状态 这是异常处理中的一条重要规则 在一个函数 中发生异常后 对象的状态应该和调用这个函数之前保持一致 以确保对象处于正确的状态中 如果对象是不可变对象 不可变对象指调用构造函数创建后就不能改变的对象 即 创建后没有任何方法可以改变对象的状态 那么异常发生后对象状态肯定不会改变 如果 是可变对象 必须在编程中注意保证异常不会影响对象状态 有三个方法可以达到这个目的 1 将可能产生异常的代码和改变对象状态的代码分开 先执行可能产生异常的代码 如果 产生异常 就不执行改变对象状态的代码 2 对不容易分离产生异常代码和改变对象状态代码的方法 定义一个 recover 方法 在异 常产生后调用 recover 方法修复被改变的类变量 恢复方法调用前的类状态 3 在方法中使用对象的拷贝 这样当异常发生后 被影响的只是拷贝 对象本身不会受到 影响 5 丢失的异常 请看下面的代码 public void method2 try method1 method1 进行了数据库操作 catch SQLException e throw new MyException 发生了数据库异常 e getMessage public void method3 try method2 catch MyException e e printStackTrace 上面 method2 的代码中 try 块捕

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论