前言: 不知道是我的状态问题还是说异常这一章有点不一样,一下午我把整个章节看完了,但是仔细想想却又感觉什么都没看进去,所以想写这篇文章来复习一下这个章节,或者说,再学习一下。
1:异常说明
JAVA 中任何方法被声明为可能抛出一个类(即存在异常说明),则在任何使用方法时都应该捕捉异常,否则会报错。具体如下:
package p459;
import p452.AException1;
import p452.AException2;
import p452.AException3;
public class ThreeException {
public static void f(int a) throws AException1,AException2,AException3{
switch(a){
case 1:throw new AException1();//这里不需要break,抛出异常就直接跳出了
case 2:throw new AException2();
case 3:throw new AException3();
default:System.out.println("你逗我呢");
}
}
static class Test{
public static void main(String[] args) {
try {//这里面的语句都要被包括在try catch 中,否则会报错
ThreeException.f(5);
ThreeException.f(1);
ThreeException.f(3);
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
此外,这段代码同样表明了一个catch语句可以捕捉多个异常,这里所有AException1,AException2,AException3都继承Exception接口
此外:异常说明不能够用来重载方法
2.异常说明并不是抛出异常的方法的专属:
并不是抛出异常的方法才能有异常说明,不抛出任何异常的方法也可以有异常说明(事实上,这种方法可以理解为,未来可能会让它抛出异常),这样做的好处是能够为这个方法占个位子。比如说,我知道这个方法未来可能会出现异常,但不知道具体出现什么异常,可以先让它具备一个异常说明,将来再来填补这个坑。
3.重新抛出异常
catch语句里面可以再抛出一个异常,即捕获了当前这个异常,再抛出一个异常(这个异常可以是当前这个异常,也可以是一个新的异常):
3.1重新抛出当前异常的引用:
语法如下:
catch(Exception e){
System.out.println("An Exception was thrown");
throw e
}
重新抛出这个异常的目的就是把这个异常交给更高一级的处理程序中,比如前面的例子中,我使用Exception,catch捕捉了三种异常,对于每一种异常可能有不同的处理程序,那么在这里,我就需要把这个异常重新抛出,把它交给更为具体的处理程序。
如果仅仅是直接重新抛出这个对象,那么对于这个异常对象的printStackTrace()方法显示的则是原来异常抛出点的异常信息。事实上很多情况下,我们并不需要这个异常被重新抛出之前的信息,那么在这种情况下我们就需要使用fillInStackTrace()方法,该方法将返回一个Throwable对象,并且把当前调用栈信息填入原先的异常对象中。具体如下:
package p462;
public class ReThrowing {
public static void f() throws Exception{
System.out.println("ReThrowing.f()");
throw new Exception("Thrown from f()");
}
public static void g() throws Exception{
try{
f();
}catch(Exception e){
System.out.println("g().e.printStackTrace()");
e.printStackTrace();
throw e;//重新抛出当前对象
}
}
public static void h() throws Exception{
try{
f();
}catch(Exception e){
System.out.println("h().e.printStackTrace()");
e.printStackTrace();
throw (Exception)e.fillInStackTrace();//使用fillInStackTrace
}
}
public static void main(String[] args) {
try{
g();
}catch(Exception e){
System.out.println("main:printStackTrace");
e.printStackTrace(System.out);
}
try{
h();
}catch(Exception e){
System.out.println("main:printStackTrace");
e.printStackTrace(System.out);
}
}
}/*out:
ReThrowing.f()
g().e.printStackTrace()
java.lang.Exception: Thrown from f()
at p462.ReThrowing.f(ReThrowing.java:6)
at p462.ReThrowing.g(ReThrowing.java:10)
at p462.ReThrowing.main(ReThrowing.java:29)
main:printStackTrace
java.lang.Exception: Thrown from f()
at p462.ReThrowing.f(ReThrowing.java:6)
at p462.ReThrowing.g(ReThrowing.java:10)
at p462.ReThrowing.main(ReThrowing.java:29)
ReThrowing.f()
h().e.printStackTrace()
java.lang.Exception: Thrown from f()
at p462.ReThrowing.f(ReThrowing.java:6)
at p462.ReThrowing.h(ReThrowing.java:20)
at p462.ReThrowing.main(ReThrowing.java:35)
main:printStackTrace
java.lang.Exception: Thrown from f()
at p462.ReThrowing.h(ReThrowing.java:24) 这里,异常没有提示f()方法,异常的轨迹是从h();开始的
at p462.ReThrowing.main(ReThrowing.java:35)*/
3 .2重新抛出另外一个异常
当你捕捉了一个异常之后,抛出另外一个异常的话,那么当前原先异常的信息就会丢失,剩下的仅仅是新的抛出点的信息,得出的效果类似于使用fillInStackTrace()。
4.异常链
弥补重新抛出另一个异常时,原先异常信息丢失的问题。cause,initCause
5.finally
finally语句总是会被运行。
因此可以在程序中,finally语句里面经常包含的是将内存之外的资源恢复到初始状态。例如已经打开的文件或网络连接,屏幕上画的图形,外部某个开关等。
在finally中使用return会造成异常丢失(即,吃掉异常),如果前一个异常还没处理就抛出下一个异常,也会造成吃掉异常的情况。下面是代码举例:
代码块
6.异常的限制
当覆盖方法时,只能抛出父类方法的异常说明里列出的那些异常(当然,子类也可以不抛出异常)。即,子类方法比父类方法有更少的异常,这样就能让父类使用的代码能够应用到子类上。
此外:异常的限制在构造器上不起作用。
7.构造器的异常
构造器在构造对象的时候,会把对象置为安全的初始状态,有时还会有诸如打开一个文件的其他动作,而这种操作的处理只能够在对象使用完毕,并且用户调用了特殊的清理方法后才能清理,如果此时构造器出现异常,那么清理工作很有可能无法正常进行。
用finally并不能解决该问题。
finally语句无论如何都会运行,如果构造器在执行过程中半途而废,也许该对象的部分还没被创建,但是finally却对它进行清理。
可以将创建对象的语句嵌套在try 语句中来解决这个问题,另外,这种清理惯用法在不抛出任何异常时也应该使用,即创建需要清理的对象的时候,应该立即进入一个try-catch语句。如下:
try{
inputFile in = new InputFile("Cleanup.java")
} catch(Exception e){
syso"InputFile construction failed"
}