Java异常的栈轨迹(Stack Trace)

3、fillInStackTrace()

  我们在前面也提到了这个方法。要说清楚这个方法,首先要讲一下捕获异常之后重新抛出的问题。在catch代码块中捕获到异常,打印栈轨迹,又重新throw出去。在上一级的方法调用中,再捕获这个异常并且打印出栈轨迹信息。这两个栈轨迹信息会一样吗?我们看一下代码:

public class TestPrintStackTrace {
    public static void f() throws Exception{
        throw new Exception("出问题啦!");
    }
    public static void g() throws Exception{
        try {
            f();
        }catch(Exception e) {
            e.printStackTrace();
            throw e;
        }
        
    }
    public static void main(String[] args) {
        try {
            g();
        }catch(Exception e) {
            e.printStackTrace();
        }
    }
}

  在main方法中捕获的异常,是在g()方法中抛出的,按理说这两个打印栈轨迹的信息应该不同,第二次打印的信息应该没有关于f的信息。但是事实上,两次打印栈轨迹信息是一样的。输出结果如下:

java.lang.Exception: 出问题啦!
    at TestPrintStackTrace.f(TestPrintStackTrace.java:3)
    at TestPrintStackTrace.g(TestPrintStackTrace.java:7)
    at TestPrintStackTrace.main(TestPrintStackTrace.java:16)
java.lang.Exception: 出问题啦!
    at TestPrintStackTrace.f(TestPrintStackTrace.java:3)
    at TestPrintStackTrace.g(TestPrintStackTrace.java:7)
    at TestPrintStackTrace.main(TestPrintStackTrace.java:16)

  也就是说,捕获到异常又立即抛出,在上级方法调用中再次捕获这个异常,打印的栈轨迹信息是一样的。原因在于没有将当前线程当前状态下的轨迹栈的状态保存进Throwabe中。现在我们引入fillInStackTrace()方法。这个方法刚好做的就是这样的保存工作。我们看一下这个方法的原型:

public Throwable fillInStackTrace()

  这个方法是有返回值的。返回的是保存了当前栈轨迹信息的Throwable对象。我们看看使用fillInStackTrace()方法处理后,打印的栈轨迹信息有什么不同,代码如下:

public class TestPrintStackTrace {
    public static void f() throws Exception{
        throw new Exception("出问题啦!");
    }
    public static void g() throws Exception{
        try {
            f();
        }catch(Exception e) {
            e.printStackTrace();
            //不要忘了强制类型转换
            throw (Exception)e.fillInStackTrace();
        }
        
    }
    public static void main(String[] args) {
        try {
            g();
        }catch(Exception e) {
            e.printStackTrace();
        }
    }
}

  输出如下:

java.lang.Exception: 出问题啦!
    at TestPrintStackTrace.f(TestPrintStackTrace.java:3)
    at TestPrintStackTrace.g(TestPrintStackTrace.java:7)
    at TestPrintStackTrace.main(TestPrintStackTrace.java:17)
java.lang.Exception: 出问题啦!
    at TestPrintStackTrace.g(TestPrintStackTrace.java:11)
    at TestPrintStackTrace.main(TestPrintStackTrace.java:17)

  我们看到,在main方法中打印栈轨迹已经没有了f相关的信息了。

  以上就是关于Java栈轨迹的一些我之前没有掌握的内容,记下来备忘。  捕获到异常时,往往需要进行一些处理。比较简单直接的方式就是打印异常栈轨迹Stack Trace。说起栈轨迹,可能很多人和我一样,第一反应就是printStackTrace()方法。其实除了这个方法,还有一些别的内容也是和栈轨迹有关的。

  1、printStackTrace()

  首先需要明确,这个方法并不是来自于Exception类。Exception类本身除了定义了几个构造器之外,所有的方法都是从其父类继承过来的。而和异常相关的方法都是从java.lang.Throwable类继承过来的。而printStackTrace()就是其中一个。

  这个方法会将Throwable对象的栈轨迹信息打印到标准错误输出流上。输出的大体样子如下:

java.lang.NullPointerException
         at MyClass.mash(MyClass.java:9)
         at MyClass.crunch(MyClass.java:6)
         at MyClass.main(MyClass.java:3)

  输出的第一行是toString()方法的输出,后面几行的内容都是之前通过fillInStackTrace()方法保存的内容。关于这个方法,我们后面会讲。

  下面看一个例子:

public class TestPrintStackTrace {
    public static void f() throws Exception{
        throw new Exception("出问题啦!");
    }
    public static void g() throws Exception{
        f();
    }
    public static void main(String[] args) {
        try {
            g();
        }catch(Exception e) {
            e.printStackTrace();
        }
    }
}

  这个例子的输出如下:

java.lang.Exception: 出问题啦!
    at TestPrintStackTrace.f(TestPrintStackTrace.java:3)
    at TestPrintStackTrace.g(TestPrintStackTrace.java:6)
    at TestPrintStackTrace.main(TestPrintStackTrace.java:10)

  在这个例子中,在方法f()中抛出异常,方法g()中调用方法f(),在main方法中捕获异常,并且打印栈轨迹信息。因此,输出依次展示了f—>g—>main的过程。

  2、getStackTrace()方法

  这个方法提供了对printStackTrace()方法所打印信息的编程访问。它会返回一个栈轨迹元素的数组。以上面的输出为例,输出的第2-4行每一行的内容对应一个栈轨迹元素。将这些栈轨迹元素保存在一个数组中。每个元素对应栈的一个栈帧。数组的第一个元素保存的是栈顶元素,也就是上面的f。最后一个元素保存的栈底元素。

  下面是一个使用getStackTrace()访问这些轨迹栈元素并打印输出的例子:

public class TestPrintStackTrace {
    public static void f() throws Exception{
        throw new Exception("出问题啦!");
    }
    public static void g() throws Exception{
        f();
    }
    public static void main(String[] args) {
        try {
            g();
        }catch(Exception e) {
            e.printStackTrace();
            System.out.println("------------------------------");
            for(StackTraceElement elem : e.getStackTrace()) {
                System.out.println(elem);
            }
        }
    }
}

  这样的输出和printStackTrace()的输出基本上是一样的,如下:

java.lang.Exception: 出问题啦!
    at TestPrintStackTrace.f(TestPrintStackTrace.java:3)
    at TestPrintStackTrace.g(TestPrintStackTrace.java:6)
    at TestPrintStackTrace.main(TestPrintStackTrace.java:10)
TestPrintStackTrace.f(TestPrintStackTrace.java:3)
TestPrintStackTrace.g(TestPrintStackTrace.java:6)
TestPrintStackTrace.main(TestPrintStackTrace.java:10)

====================================分割线================================

最新内容请见作者的GitHub页:http://qaseven.github.io/

时间: 2024-08-13 03:35:39

Java异常的栈轨迹(Stack Trace)的相关文章

JAVA异常是否对于性能有影响

  在对OneAPM的客户做技术支持时,我们常常会看到很多客户根本没意识到的异常.在消除了这些异常之后,代码运行速度与以前相比大幅提升.这让我们产生一种猜测,就是在代码里面使用异 常会带来显著的性能开销.因为异常是错误情况处理的重要组成部分,摒弃是不太可能的,所以我们需要衡量异常处理对于性能影响,我们可以通过一个实验看看异常处理的对于性能的影响. 实验 我的实验基于一段随机抛出异常的简单代码.从科学的角度,这并非完全准确的测量,同时我也并不了解HotSpot 编译器会对运行中的代码做何动作.但无

浅谈JAVA 异常对于性能的影响_java

在对客户做技术支持时,我们常常会看到很多客户根本没意识到的异常.在消除了这些异常之后,代码运行速度与以前相比大幅提升.这让我们产生一种猜测,就是在代码里面使用异常会带来显著的性能开销.因为异常是错误情况处理的重要组成部分,摒弃是不太可能的,所以我们需要衡量异常处理对于性能影响,我们可以通过一个实验看看异常处理的对于性能的影响. 实验 我的实验基于一段随机抛出异常的简单代码.从科学的角度,这并非完全准确的测量,同时我也并不了解HotSpot 编译器会对运行中的代码做何动作.但无论如何,这段代码应该

JAVA 异常对于性能的影响

在对OneAPM的客户做技术支 持时,我们常常会看到很多客户根本没意识到的异常.在消除了这些异常之后,代码运行速度与以前相比大幅提升.这让我们产生一种猜测,就是在代码里面使用异 常会带来显著的性能开销.因为异常是错误情况处理的重要组成部分,摒弃是不太可能的,所以我们需要衡量异常处理对于性能影响,我们可以通过一个实验看看异 常处理的对于性能的影响. 实验 我的实验基于一段随机抛出异常的简单代码.从科学的角度,这并非完全准确的测量,同时我也并不了解HotSpot 编译器会对运行中的代码做何动作.但无

java.lang.Exception: DEBUG -- CLOSE BY CLIENT STACK TRACE 的理解

[2013-12-06 11:06:21,715] [C3P0PooledConnectionPoolManager[identityToken->2tl0n98y1iwg7cbdzzq7a|719f1f]-HelperThread-#2] DEBUG - com.mchange.v2.c3p0.impl.NewPooledConnection@484c6b closed by a client. java.lang.Exception: DEBUG -- CLOSE BY CLIENT STA

线程-java Stack trace of thread "Timer-0":

问题描述 java Stack trace of thread "Timer-0": 项目debug模式下 ,重新编译时出现以下错误: 严重: Stack trace of thread "Timer-0": java.lang.Object.wait(Native Method) java.util.TimerThread.mainLoop(Timer.java:552)java.util.TimerThread.run(Timer.java:505) 请大神指点

Java异常及相关调用性能测试

问题 参数校验问题 见到有人讨论java里的异常性能是好是坏,在业务代码里要不要用异常处理. 例如一些请求参数,到底是应该人工用if/else判断,还是通过异常统一处理,还是通过注解校验,抑或是其他方式呢? 这些方式对系统的性能会有什么实际影响呢? 日志运行信息问题 一般认为,java异常之所以慢,是因为需要获取当前的运行栈信息,而异常机制本身是常规性能消耗. 进而想到,一些通用的日志框架,比如log4j,logback,都是通过运行栈获取抛出异常的代码方法和运行行数的,官方文档也标注此类信息的

Java 异常设计最佳实践

关于异常 在讲Java异常实践之前,先理解一下什么是异常.到底什么才算是异常呢?其实异常可以看做在我们编程过程中遇到的一些意外情况,当出现这些意外情况时我们无法继续进程正常的逻辑处理,此时我们就可以抛出一个异常. 广义的讲,抛出异常分三种不同的情况: 编程错误导致的异常 :在这个类别里,异常的出现是由于代码的错误(譬如NullPointerException.IllegalArgumentException.IndexOutOfBoundsException ).代码通常对编程错误没有什么对策,

Java异常

在我参与过的一些Java项目.或者阅读过Java代码中,异常的使用或多或少都有点问题,有些甚至是误用.可能很多人都了解异常的基本知识,但是使用的时候往往过于随意,然而这样的随意往往并不能造成什么严重的问题,所以很多程序员,尤其是初级的程序员也就没有在意它,于是代码中总是会有那些让人不舒服的异常使用代码:另一方面,Java异常类型中包括Checked Exception(Runtime Exception)和Unchecked Exception,这两中异常的使用也颇具争议. 基于上述问题,总结一

Java异常封装(自己定义错误码和描述,附源码)

版权声明:本文为博主原创文章,转载注明出处http://blog.csdn.net/u013142781 目录(?)[+] 真正工作了才发现,Java里面的异常在真正工作中使用还是十分普遍的.什么时候该抛出什么异常,这个是必须知道的. 当然真正工作里面主动抛出的异常都是经过分装过的,自己可以定义错误码和异常描述. 下面小宝鸽就为大家介绍一个Java异常简单封装的例子. 在给出异常分装例子之前,需要给大家普及Java里面的checked异常和unchecked异常的个概念. 一.checked异常