Java编程思想学习笔记-第12章
Table of Contents
1 第十二章-通过异常处理错误
1.1 基本异常
- 异常情形是指组织当前方法或者作用域继续执行的问题.
- 普通问题是指在当前环境下能得到足够的信息,总能处理这个错误.
1.2 捕获异常
1.2.1 异常处理程序
- 异常处理理论有两种基本模型.
- 终止模型
- 恢复模型
- Java支持终止模型,将假设错误非常关键,以至于程序无法返回到异常发生的地方继续执行.一旦异常抛出,就表名错误无法挽回,也不能回来继续执行.
- 另一种是恢复模型,异常处理程序的工作是修正错误,然后重新尝试调用出问题的方法.并认为第二次能成功.
- Java可以把try块放在while循环中,直到得到满意的结果.
1.3 异常说明
- 异常说明使用了附加的关键字
throws
,后面接一个所有潜在异常类型的列表,方便客户端程序员查看.
1.4 捕获所有异常
- 通过捕获异常类型的基类Exception就可以处理所有类型的异常.
- Exception从基类Throwable继承的方法有
- String getMessage(): 详细信息
- String getLocalizedMessage(): 本地语言描述详细信息
- void printStackTrace(): 调用栈显示了"把你带到异常抛出地点"的方法调用序列,输出到标准输出.
- void printStackTrace(PrintStream): 调用栈显示了"把你带到异常抛出地点"的方法调用序列,输出到要输出的流.
- void printStackTrace(java.io.PrintWriter): 调用栈显示了"把你带到异常抛出地点"的方法调用序列,输出到要输出的流.
- Throwable fillInStackTrace(): 用于在Throwable对象的内部记录栈帧的当前状态.
1.4.1 栈轨迹
- printStackTrace()方法所提供的信息可以通过getStackTrace()方法直接访问.
- getStackTrace()方法返回一个由根轨迹中的元素所构成的数组,每一个元素都表示栈中的一帧.
1.4.2 重新抛出异常
- 重新抛出异常会把异常抛给上一级环境中的异常处理程序,同一个try块的后续catch字句将忽略.
- 异常对象的所有信息都得以保持,所以高一级环境中捕获此异常的处理程序可以从这个异常对象中得到所有消息.
- 如果只是把当前异常对象重新抛出,那么printStackTrace()方法显示的将是原来异常抛出点的调用栈信息,而非重新抛出点的信息.
- 想要更新这个信息,可以调用fillInStackTrace()方法,这将返回一个Throwable对象,它是通过把当前调用栈信息填入原来那个异常对象而建立的.
1.4.3 异常链
- 捕获一个异常后抛出另一个异常,并且希望把原始异常的信息保存下来,被称为异常链.
- Throwable子类在构造器中可以接受一个cause(因由)对象作为参数.这个cause就是用来表示原始异常,这样通过原是异常传递给新的异常,使
得即使在当前位置创建并抛出了新的异常,也能通过这个异常链追中岛异常最初发生的位置.
- 在Throwable子类中,只有三种基本异常类提供了带cause参数的构造器
- Error(用于Java虚拟机报告系统错误)
- Exception
- RuntimeException
- 如果要其他类型的异常链接起来,应该使用initCause()方法而不是构造器.
1.5 Java标准异常
- Throwable这个Java类被用来表示任何可以作为异常被抛出的类.
- Throwable可分为两种类型(从Throwable继承而得到的类型):
- Error用来表示编译时和系统错误
- Exception是可以被抛出的基本类型
1.5.1 RuntimeException
- 属于运行时异常的类型有很多,它们会自动被Java虚拟机抛出,这些异常都是从RuntimeException中继承而来的.
- RuntimeException是
"不受检查异常"
,这种异常属于错误,将被自动捕获. - 只能在代码中忽略RuntimeException(及其子类)类型的异常,其他类型的异常处理都是由编译器强制实施的.
- RuntimeException代表的是编程错误:
- 无法预料的错误.比如你从控制范围之外传递进来的null引用.
- 作为程序员,应该在代码中进行检查的错误.
1.6 使用finallly进行清理
- Java中使用finally一般把除内存之外的资源恢复到它们的初始状态时.
- 异常处理机制会在跳到更高一层的异常处理程序之前,执行finally子句.
1.6.1 在return中使用finally
- 因为finally子句总是会执行,所以在一个方法中,可以从多个点返回,并且可以保证重要清理工作仍旧会执行.
- return语句返回之前会执行finally子句的代码块.
1.6.2 异常丢失的情况
- 前一个异常还没处理就抛出下一个异常,没有catch捕获异常,使用finally抛出下一个异常.
- 在finally中加入return语句,没有用catch语句捕获异常.
1.7 异常的限制
- 不能基于异常说明来重载方法.
- 子类抛出的异常要 小于父类.
1.8 构造器
- 构造器抛出异常要格外注意清理方法是否有必要调用
package exceptions; import java.io.BufferedReader; import java.io.FileNotFoundException; import java.io.FileReader; import java.io.IOException; public class InputFile { private BufferedReader in; public InputFile(String fname) throws Exception { try { in = new BufferedReader(new FileReader(fname)); // Other code that might throw exceptions. } catch (FileNotFoundException e) { System.out.println("Could not open " + fname); // Wasn‘t open, so don‘t close it throw e; } catch (Exception e) { // All other exceptions must close it try { in.close(); } catch (IOException e2) { System.out.println("in.close() unsuccessful"); } //Rethrow throw e; } finally { //Don‘t close it here!!! } } public String getLine() { String s; try { s = in.readLine(); } catch (IOException e) { throw new RuntimeException("readLine() failed"); } return s; } public void dispose() { try { in.close(); System.out.println("dispose() successful"); } catch (IOException e) { throw new RuntimeException("in.close() failed"); } } }
- 设计异常时,直接向上层抛出的却能简化编程.
- 对于在构造阶段可能会抛出异常,并且要求清理的类,最安全的使用方式是使用嵌套的try语句.
- 这种通用的清理惯用法在构造器不抛出任何异常时也应该使用,基本规则是:在创建需要清理的对象之后,立即进入一个
try-finally
语句块.
1.9 异常匹配
- 抛出异常的时候,异常处理系统会安装代码书写顺序找出"最近的"处理程序.
1.10 其他可选的方式
- "被检查异常"强制你在还没准备好处理错误的时候被迫加上catch子句,这就导致了吞食则有害的问题.异常被吞食了.
1.10.1 把异常传递给控制台
- 最简答而又不用写多少代码就能保护异常信息的方法,就是把它们从main()传递到控制台.
1.10.2 把"被检查的异常"转换为"不检查的异常"
- 当我们不知道该怎么处理这个异常,但是也不想把它"吞"了,或者打印一些无用的信息,可以使用异常链的思路解决.
- 把"被检查的异常"包装进RuntimeException里面,避免异常被"吞".
1.11 异常使用指南
- 在恰当的级别处理问题.(在知道该如何处理的情况下才捕获异常)
- 解决问题并且重新调用产生异常的方法.
- 进行少许修补,然后绕过异常发生的地方继续执行.
- 用别的数据进行计算,以代替方法预计会返回的值.
- 把当前环境下能做的事情尽量做完,然后把相同的异常重抛到更高层.
- 把当前环境下能做的事情尽量做完,然后把不同的异常抛到更高层.
- 终止程序.
- 进行简化.
- 让类库和程序更加安全.