Java学习笔记之异常(三)

1.栈轨迹

     上篇博客里说到了异常的一些常用方法,其中有三个方法可以打印异常的调用信息,printStackTrace()所提供的信息可通过getStackTrace()来直接访问,这个方法将返回一个由栈轨迹中的元素构成的数组,其中每个元素代表栈中的一帧,元素0是栈顶元素,并且是调用序列中的最后一个方法调用(这个Throwable被创建和抛出之处),数组中的第一个元素和栈底是调用序列中的第一个方法调用。

class StackTrace 
{
void f() throws Exception{
    throw new Exception("Throw from f()");
}
void g() throws Exception{
    f();
}
public static void main(String[] args) 
{
StackTrace st=new StackTrace();
try{
    st.g();
}catch(Exception e){
    for(StackTraceElement element:e.getStackTrace()){
    System.out.println("Class : "+element.getClassName()+
     " Method : "+element.getMethodName());
}
e.printStackTrace();
}
}
}

Output:

Java学习笔记之异常(三)_第1张图片

可以看到,main方法调用g方法,g方法再调用f方法,这就是异常的栈轨迹。

2.重新抛出异常

   异常是像上面那样根据方法依次调用的,而异常的抛出是抛向其上一个方法交由它处理,上面的代码中,g的异常交由f处理,但是f没有进行捕获,就向上抛出了。有时可以在g中捕获之后,再次抛出,那么这次抛出的异常是由它的上一级来处理,也就是main方法。

void g() throws Exception{
    try{
   f();
}catch(Exception e){
   System.out.println("g() handle exception from f() then throw again");
   throw e;//抛出的异常为从f中捕获的异常,异常信息没有变化
   //throw new Exception("Throw from g()");//抛出的异常为一个新的异常,异常信息与从f中抛出的异常不同
   //throw (Exception)e.fillInStackTrace();//抛出的异常为捕获的异常,但是却更新了异常信息。
   }
}

将1中的代码中的g方法改成上述代码,并将main方法中catch中的foreach注释掉,运行结果如下:

因为在g中抛出的异常是从f中捕获的异常,所以异常信息并没有与1中的发生变化,但是将上述代码中的第一行注释段去掉后,也就是在g中抛出了一个新的异常,那么main捕获的自然也是一个新的异常,异常信息自然也不一样了,结果如下:

如果既想重新抛出之前捕获的异常又想更新栈轨迹信息,那么可以使用fillInStackTrace放。将上述代码第二行代码注释去掉后,运行结果如下:

3.异常链

      常常会想要在捕获一个异常后抛出另一个异常,并且希望把原始异常的信息保存下来,这被称为异常链。JDK 1.4之后,所有的Throwable子类在构造器中接受一个cause对象作为参数,这个cause就用来表示原始异常,这样通过把原始异常传递给新的异常,使得即使在当前位置创建并抛出了新的异常,也能通过这个异常追踪到异常最初发生的位置。

        将上述g方法中的异常处理程序抛出的异常代码改为

throw new Exception("Throw from g",e);

运行结果为:

Java学习笔记之异常(三)_第2张图片

可以看到除了新的异常外,还有cause代表的原始异常。

在Throwable子类中,只有三种基本的异常类提供了带cause参数的构造器,它们是Error(用于Java虚拟机报告系统错误)、Exception以及RuntimeException。如果要把其他的异常链接起来,应该使用initCause()方法而不是构造器。

void g() throws IOException{
    try{
   f();
}catch(Exception e){
   System.out.println("g() handle exception from f() then throw again");
   throw (IOException)new IOException("Throw from g").initCause(e);
   }
}

将g方法改成上述代码,抛出IOException,并且使用initCause方法将原始异常作为参数。运行结果如下:

Java学习笔记之异常(三)_第3张图片

4.总结

      1)打印异常栈轨迹可以使用printStackTrace和getStackTrace方法等进行处理。

      2)重新抛出捕获的异常与抛出一个新的异常的区别;既要更新栈调用信息,又想重新抛出之前捕获的异常需要使用fillInStackTrace方法。

      3)异常链是在捕获一个异常后抛出一个新的异常,并将原始异常的信息保存下来。

Error、Exception和RuntimeException可以使用构造器将cause作为参数,而其他的异常则需要使用initCause方法将原始异常作为参数。

你可能感兴趣的:(栈轨迹,异常重新抛出,异常链)