12.4java
以前程序里写日志不清楚怎么把printStackTrace()输出的内容写到日志里,仅仅是写getMessage()信息少了很多。在本节的例子中给出了一个方法:程序员
StringWriter sw = new StringWriter();编程
PrintWriter pw = new PrintWriter(sw);数组
e.printStrackTrace(pw);函数
logger.error(sw.toString());测试
12.5编码
能够声明方法将异常抛出,但实际上该方法并不抛出异常。这样作的好处是为异常先占一个位子,之后能够抛出这种异常而不用修改方法声明。spa
在编译时被强制检查的异常称为被检查的异常。.net
练习8题中说“抛出练习3里定义的异常”错了,应该是练习4中定义的异常。中有个细节须要注意:声明方法时标识了抛出的异常,即便方法内部并无真正抛出此异常,调用该方法的时仍是要处理异常,不然编译器会报错。日志
12.6
Exception是与编程有关的全部异常类的基类。它从Throwable类中继承了一些方法:
String getMessage()
String getLocalizedMessage()
获取异常信息和用本地语言表示的异常信息。
void printStackTrace()
void printStackTrace(PrintStream)
void printStackTrace(PrintWriter)
打印信息和调用栈轨迹。第一个版本输出到标准错误流,后两个版本能够选择要输出的流。
Throwable fillInStrackTrace()这个方法的做用是对调用的对象从新填充调用栈,使得调用栈看起来和新建立的异常是相同的。通过编码测试,方法返回的Throwable的对象和调用对象是同一个对象,看来只是改变了调用栈的数据,如下是测试代码:
public class FilInStackTraceTest { public void f() throws Exception { throw new Exception(); } public void g() { try { f(); } catch (Exception e) { e.printStackTrace(); Exception e1 = (Exception)e.fillInStackTrace(); System.out.println(e1 == e); e.printStackTrace(); e1.printStackTrace(); } } public static void main(String[] args) { FilInStackTraceTest test = new FilInStackTraceTest(); test.g(); } }
输出结果以下:
java.lang.Exception
at com.sfauto.exception.FilInStackTraceTest.f(FilInStackTraceTest.java:6)
at com.sfauto.exception.FilInStackTraceTest.g(FilInStackTraceTest.java:11)
at com.sfauto.exception.FilInStackTraceTest.main(FilInStackTraceTest.java:23)
java.lang.Exception
at com.sfauto.exception.FilInStackTraceTest.g(FilInStackTraceTest.java:14)
at com.sfauto.exception.FilInStackTraceTest.main(FilInStackTraceTest.java:23)
java.lang.Exception
at com.sfauto.exception.FilInStackTraceTest.g(FilInStackTraceTest.java:14)
at com.sfauto.exception.FilInStackTraceTest.main(FilInStackTraceTest.java:23)
true
从最后一行能够看出,e与e1是一个对象,调用fillInStackTrace()方法以前和以后e.printStackTrace()的打印结果不一样,而调用filllInStackTrace()方法以后e与e1printStackTrace()的打印同。因而可知fillInStackTrace()是改变了调用对象的调用栈。
printStackTrace()方法所提供的信息能够经过getStackTrace()方法来获取,此方法将返回一个数组,每个元素都表示栈中的一帧。元素0是栈顶元素,是调用序列中的最后一个方法调用(即Throwable被建立和抛出之处,离异常最近的方法),数组中最后一个元素是栈底元素,即调用的最外层方法。
经常会想要在捕获一个异常后抛出另外一个异常,而且但愿把原始异常的信息保存下来,这被称为异常链。Throwable和它的一些子类提供了带有参数Throwable cause的构造函数来维持异常链。书中说只有Error、Exception和RuntimeException提供这个构造函数,好像说的不对,至少我知道的SQLException和IOException都有这种构造函数。在没有此种构造函数的状况下能够调用initCause()方法来达到相同的效果。
12.7
Throwable这个Java类被用来表示任何能够做为异常被抛出的类。Throwable对象能够分为两种类型:Error用来表示编译时错误和系统错误,除特殊状况外不用关心;Exception是与编程打交道的基本异常类型,在Java类库、用户方法以及运行时故障中均可能抛出Exception异常。因此Java程序员关心的基本类型一般是Exception。
异常的基本概念是用名称表明发生的问题,异常的名称能够望文生义。异常并不是全在java.lang包中,还存在于util、net和io包中。
有一些问题属于Java的标准运行时检测的一部分,它们会自动被Java虚拟机抛出,因此没必要在方法的异常说明中把它们列出来,这样的异常被称为不受检查的异常(也有叫运行时异常的吧),它们都是RuntimeException的子类。这种异常属于错误,不强制要求手动捕获,能够再本身的代码中抛出这种异常。若是不捕获这种异常,它会穿越全部的执行路径直达main()方法,在主程序退出前将调用异常的printStackTrace()方法。
RuntimeException表明的是编程错误:
(1)没法预料的错误。好比从你的代码控制范围以外传递近来的Null引用;
(2)应该在代码中检查的错误,好比数组越界。
12.8
Java中的异常不容许回到异常抛出的地点,若是想实现这一功能能够把try快放到循环中,这就创建了一个“程序继续执行以前必需要达到”的条件,还能够加入一个static类型的计数器或者别的装置,使循环在放弃之前能尝试必定的次数。
当涉及到break和continue语句的时候,finally子句也会获得执行。
当try块中包含return语句,其后的finally块也会被执行。
12.8.3小节中做者演示了两种不恰当的方法使得一些异常被忽略,值得注意
第一种问题的解决方法
12.9
当覆盖方法时,只能抛出在基类方法的异常说明中列出的异常,能够少抛出或不抛出这些异常,也能够抛出这些异常的子类,或者不抛出异常。可是不能添加新的异常。即某个方法的异常说明范围能够变小可是不能变大。
异常限制对构造器不起做用。子类的构造器能够抛出任何新异常。可是由于基类构造器必须以这样或那样的方法被调用,派生类构造函数的异常说明必须包含基类构造函数的异常说明。
派生类构造函数不能捕获基类构造函数抛出的异常。
12.10
对于抛出异常的构造函数,做者认为应该这样处理:在一个单独try-catch语句中构造对象,一旦对象构形成功(即构造函数未抛出异常)用另外的嵌套try-catch-finally语句写其余功能,在finally中记得清理该对象的资源。
12.11
练习25中,子类覆盖了父类的方法而且抛出了比父类方法更窄的异常,当咱们建立了一个子类的对象,并将其向上转型为父类,调用该方法编译器会强制要求捕获父类的异常。
12.12
对于一些不知道怎么处理的被检查异常,做者推荐两种办法:
(1)在main()函数中抛出这些异常;
(2)利用异常链,把被检查异常包装成RuntimeException抛出
throw new RuntimeException(e);
补充(前两条来自《Java 8编程参考官方教程》,第三条官方教程看不明白,参考了如下博客http://blog.csdn.net/jackiehff/article/details/17839225,以前的例子理解了,最后的论述依旧不是很懂)
1.7版本,异常系统添加的三个新特性:
一、带资源的try
这种特性有时被称为自动资源管理(Automatic Resource Management,ARM)
try语句的形式:
try(资源定义和初始化) {
}
在资源定义和初始化语句中声明的变量当try语句块结束时,自动释放资源。只有实现了AutoCloseable接口的资源才能使用带资源的try语句,该接口定义了close()方法,try语句块结束的时候会调用资源的close()方法。
try语句中声明的资源被隐式的声明为fianl,这意味着在建立资源变量后不能将其余变量赋值给该引用。另外,资源的做用域局限于带资源的try语句。
能够再一条try语句中管理多个资源。为此,只须要简单的使用分号分隔每一个资源便可。
关闭资源的close()方法也可能抛出异常,使用带资源的try语句时,当try语句块中抛出异常同时close()方法也抛出异常,close()方法抛出的异常会被抑制,但它并无丢失,而是被添加到第一个异常的抑制列表中,使用Throwable类定义的getSupperessed()方法能够获取抑制异常列表。
二、多重捕获
容许经过相同的catch子句捕获多个异常,使用操做符 | 分隔每一个异常。每一个多重捕获参数都被隐式的声明为final,所以不能赋予它新的值。正常的捕获并无final的限制,我想是由于多重捕获形式以下:
catch(IOException | ArrayIndexOutOfBoundsException e ) {
}
若是要在catch语句块中从新赋值,编译器搞不清楚e究竟是第一个类型仍是第二个类型。
三、从新抛出精确的异常
考虑下面的例子:
static class FirstException extends Exception { } static class SecondException extends Exception { } public void rethrowException(String exceptionName) throws Exception { try { if (exceptionName.equals("First")) { throw new FirstException(); } else { throw new SecondException(); } } catch (Exception e) { throw e; } }
这个例子的 try
块既能够抛出 FirstException
也能够抛出 SecondException
。假定你想要在rethrowException方法声明中的 throws
子句中指定异常类型为这两种类型,在Java SE 7以前的版本中你不能这么作。由于 catch
子句的异常参数e是 Exception
类型,而且 catch块从新抛出这个异常参数 e,因此你只能在rethrowException方法声明中的 throws
子句中指定异常类型为 Exception
。
然而在Java SE 7中, 你能够在rethrowException方法声明中的 throws
子句中指定异常类型为 FirstException
和 SecondException
。 Java SE 7编译器能够探测到由 throw e
语句抛出的异常必须来自于 try
块, 而且 try
块抛出的异常只能是 FirstException
和 SecondException
。即便 catch
子句的异常参数e的类型是 Exception
,编译器也能够探测到它是 FirstException
仍是 SecondException
的实例:
public void rethrowException(String exceptionName) throws FirstException, SecondException { try { // ... } catch (Exception e) { throw e; } }
若是 catch
块中的 catch
参数被指定给另外一个值,那么这种分析失效。然而,若是的 catch
参数被指定给另外一个值, 你必须在方法声明的 throws
子句中指定异常类型为 Exception
。
具体说来,在Java SE 7及后续版本中, 当你在一个 catch
子句中声明一个或多个异常类型而且从新抛出由这个 catch
块处理的异常,编译器会验证从新抛出的异常类型是否知足如下条件:
try
块能够抛出它。catch
块没有办法处理它。catch
子句其中一个异常参数的子类或者超类。