以下是第八篇读书笔记的具体内容。
- 所有标准异常类都有两个构造器:一个是默认构造器,另一个是接受字符串作为参数,以便能把相关信息放入异常对象的构造器
- 监控区域(guarded region):一段可能产生异常的代码,并且后面跟着处理这些异常的代码
- 异常处理理论上有两种基本模型:终止模型和恢复模型。Java支持终止模型
- 通常,使用e.printStackTrace()会将输出重定向到System.err,即标准错误流,可以用e.printStackTrace(System.out)的方式将输出重定向到System.out
- Throwable.getMessage()方法,覆盖此方法即可控制异常类的printStackTrace()的行为,类似于普通类的toString()方法
- 异常说明:使用throws关键字,加上一个所有潜在异常类型的列表
- 异常要么被处理,要么就是被抛出
- Java中所有的异常或者错误都继承Throwable,可以分为三类:Error,Exception和RuntimeException
- 在编译时被强制检查的异常称为被检查的异常;而不受检查的异常默认是一定在程序中的,常见的类型包括:NullPointerException空指针错误,ArrayIndexOutOfBoundsException数组越界等
- 可以使用getStackTrace()方法来获得每一项详细异常信息,然后利用foreach语法遍历,实例如下:for(StackTraceElement ste : e.getStackTrace()){ ……};
- 重新抛出异常的时候,可以使用fillInStackTrace()方法返回一个Throwable对象,它是通过把当前调用栈信息填入原来那个异常对象而建立的
- 只要是通过new在堆上创建的对象,垃圾回收器都能够自动把它们清理掉
- 异常链:在捕获一个异常后抛出另一个异常,并且把原始异常的信息保存下来
- Throwable的子类当中,Error,Exception和RuntimeException都提供了带cause参数的构造器,这个cause是用来表示原始异常,这样通过把原始异常传递给新的异常,使得即使在当前位置创建并抛出了新的异常,也能通过这个异常链追踪到异常最初发生的位置
- 但是绝大多数异常并没有带cause参数的构造器,此时应该使用initCause()方法而不是构造器
- 编写涉及循环的toString()方法,通常都会想到使用StringBuilder
- finally子句:甚至在异常没有被当前的异常处理程序捕获的情况下,异常处理机制也会在跳到更高一层的异常处理程序之前,执行finally子句
- 如果把finally子句和带标签的break及continue配合使用,在Java里就没必要使用goto语句了。因为finally子句一定会被执行,即使从break及continue跳出也会执行
- Java异常当中的缺憾:前一个异常还没处理就抛出下一个异常,就会造成异常丢失。比如只有try子句和finally子句,那么try子句中抛出一个异常,finally子句中抛出一个异常,就会导致try子句当中的异常丢失;或者直接从finally子句中返回,那么会静默所有抛出的异常
- 异常的限制:当覆盖方法的时候,只能抛出在基类方法的异常说明里列出的那些异常,即可以变小但是不能变大。一切都是为了实现类型可替代
- 实现的接口里的方法不能改变基类中方法的异常说明
- 异常限制对构造器不起作用,及派生类的构造器可以抛出任何异常,而不必理会基类构造器所抛出的异常。但是,派生类构造器的异常说明里必须包含基类构造器的异常说明
- 通用的清理习惯用法:在创建需要清理的对象之后,立即进入一个try-finally语句块,这样finally子句能够保证一定执行清理
- 异常匹配:按照代码的书写顺序找出最近的处理程序,找到以后就不再继续查找
- 反射和泛型就是用来弥补静态类型检查所带来的过多限制
- 将被检查的异常转换为不检查的异常:将被检查的异常作为参数传入不受检查的异常的构造器当中即可
- 总而言之,异常有时候存在的一个问题就是:要求你捕获并处理这个异常,但是这个时候你又不知道该如何处理这个异常!
- 异常的一个优点是,使得我们可以在某处集中精力处理我们要解决的问题,而在另一处处理我们编写的这段代码中产生的错误。即实现了两者的分离
以上就是关于异常的笔记内容,如有错误,敬请指出,谢谢!