本文转自wawlian

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

1.printStackTrace()

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

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

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

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

下面看一个例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
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();
        }
    }
}

这个例子的输出如下:

1
2
3
4
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()访问这些轨迹栈元素并打印输出的例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
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()的输出基本上是一样的,如下:

1
2
3
4
5
6
7
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)

3.fillInStackTrace()

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
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的信息。但是事实上,两次打印栈轨迹信息是一样的。输出结果如下:

1
2
3
4
5
6
7
8
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()方法。这个方法刚好做的就是这样的保存工作。我们看一下这个方法的原型:

1
public Throwable fillInStackTrace()

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
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();
        }
    }
}

输出如下:

1
2
3
4
5
6
7
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相关的信息了。

  4.fillInStackTrace()续

fillInStackTrace这个方法还是很有特点的,竟然能够一直追踪调用自己的那些方法,甚至java的行数,本想仔细看看源代码的,没想到竟然是native的方法,而且api文档中只有一句话,实在是让人很难知道它的真正用法。do了一下research之后,有一些心得。

package 捕捉异常;

public class FillInStackTraceDemo {     

         Throwable th =  new Throwable();

         public FillInStackTraceDemo() {
System.out.println("in constructor");
} public void a()
{
c();
} public void b()
{
System.out.println("in b");
th.fillInStackTrace();
th.printStackTrace(System.out);
System.out.println("in b");
} public void c()
{
b();
th.fillInStackTrace();
System.out.println("in c");
th.printStackTrace(System.out);
System.out.println("in c");
} public static void main(String [] args)
{
FillInStackTraceDemo t3 = new FillInStackTraceDemo();
t3.a();
}
} /*in constructor
in b
java.lang.Throwable
at 捕捉异常.FillInStackTraceDemo.b(FillInStackTraceDemo.java:19)
at 捕捉异常.FillInStackTraceDemo.c(FillInStackTraceDemo.java:26)
at 捕捉异常.FillInStackTraceDemo.a(FillInStackTraceDemo.java:13)
at 捕捉异常.FillInStackTraceDemo.main(FillInStackTraceDemo.java:37)
in b
in c
java.lang.Throwable
at 捕捉异常.FillInStackTraceDemo.c(FillInStackTraceDemo.java:27)
at 捕捉异常.FillInStackTraceDemo.a(FillInStackTraceDemo.java:13)
at 捕捉异常.FillInStackTraceDemo.main(FillInStackTraceDemo.java:37)
in c*/

   fillInStackTrace每次执行的时候,会清空原来的栈内的trace信息。然后在当前的调用位置处重新建立trace信息, 所以在方法b()中printStackTrace的执行结果跟c()中的是不一样的。

以上就是关于Java栈轨迹的一些我之前没有掌握的内容,记下来备忘。

基础知识《十四》Java异常的栈轨迹fillInStackTrace和printStackTrace的用法的更多相关文章

  1. Java异常的栈轨迹fillInStackTrace和printStackTrace的用法

    本文转自wawlian 捕获到异常时,往往需要进行一些处理.比较简单直接的方式就是打印异常栈轨迹Stack Trace.说起栈轨迹,可能很多人和我一样,第一反应就是printStackTrace()方 ...

  2. 正则表达式、Calendar类、SimpleDateFormat类、Date类、BigDecimal类、BigInteger类、System类、Random类、Math类(Java基础知识十四)

    1.正则表达式的概述和简单使用 * A:正则表达式(一个字符串,是规则)     * 是指一个用来描述或者匹配一系列符合某个语法规则的字符串的单个字符串.其实就是一种规则.有自己特殊的应用. * B: ...

  3. Android学习之基础知识十四 — Android特色开发之基于位置的服务

    一.基于位置的服务简介 LBS:基于位置的服务.随着移动互联网的兴起,这个技术在最近的几年里十分火爆.其实它本身并不是什么时髦的技术,主要的工作原理就是利用无线电通讯网络或GPS等定位方式来确定出移动 ...

  4. oracle 基础知识(十四)----索引扫描

    (1)索引唯一扫描(index unique scan) 通过唯一索引查找一个数值经常返回单个ROWID.如果该唯一索引有多个列组成(即组合索引),则至少要有组合索引的引导列参与到该查询中,如创建一个 ...

  5. ASP.NET Core 2.2 基础知识(十四) WebAPI Action返回类型(未完待续)

    要啥自行车,直接看手表 //返回基元类型 public string Get() { return "hello world"; } //返回复杂类型 public Person ...

  6. IM开发基础知识补课(四):正确理解HTTP短连接中的Cookie、Session和Token

    本文引用了简书作者“骑小猪看流星”技术文章“Cookie.Session.Token那点事儿”的部分内容,感谢原作者. 1.前言 众所周之,IM是个典型的快速数据流交换系统,当今主流IM系统(尤其移动 ...

  7. Bootstrap<基础二十四> 缩略图

    Bootstrap 缩略图.大多数站点都需要在网格中布局图像.视频.文本等.Bootstrap 通过缩略图为此提供了一种简便的方式.使用 Bootstrap 创建缩略图的步骤如下: 在图像周围添加带有 ...

  8. Java15-java语法基础(十四)抽象类

    Java15-java语法基础(十四)抽象类 一.抽象类的作用 三个类都有"执行任务"的方法,分别在这三个类中进行定义,因此需要重复编写代码,降低了程序开发效率,且增加了程序出现错 ...

  9. 基础知识《十》java 异常捕捉 ( try catch finally ) 你真的掌握了吗?

    本文转载自  java 异常捕捉 ( try catch finally ) 你真的掌握了吗? 前言:java 中的异常处理机制你真的理解了吗?掌握了吗?catch 体里遇到 return 是怎么处理 ...

随机推荐

  1. 最接近WeChat的全屏自定义相机(Custom Camera)

    代码地址如下:http://www.demodashi.com/demo/13271.html 一.需求 最接近WeChat的全屏自定义相机(Custom Camera),拍照和预览都是全屏尺寸.使用 ...

  2. PHP-四种解析XML文件的方法

    XML处理是开发过程中经常遇到的,PHP对其也有很丰富的支持,本文只是对其中某几种解析技术做简要说明,包括:Xml parser, SimpleXML, XMLReader, DOMDocument. ...

  3. javascript开源大全

    javascript开源大全 Ajax框架-jQuery 可视化HTML编辑器-CKEditor 国产jQuery-UI框架-(jUI)-DWZ 网页开发FireFox插件-Firebug 服务器端的 ...

  4. Freeswitch中文用户手册(第四章 SIP)----2

    通过 B2BUA 呼叫 在真实世界中,bob 和 alice 肯定要经常改变位置,那么它们的 SIP 地址也会相应改变,并且,如果他们之中有一个或两个处于 NAT 的网络中时,直接通信就更困难了.所以 ...

  5. vivado笔记

    Vivado主界面 Vivado套件,相当于把ISE.ISim.XPS.PlanAhead.ChipScope和iMPACT等多个独立的套件集合在一个Vivado设计环境中,在这个集合的设计流程下,不 ...

  6. 所需即所获:像 IDE 一样使用 vim

    所需即所获:像 IDE 一样使用 vim 转载 yangyangwithgnu@yeah.net2015-11-08 10:05:53 谢谢 捐赠:支付宝 yangyangwithgnu@yeah.n ...

  7. linux下创建用户(转)

    转自 http://www.cnblogs.com/ylan2009/articles/2321177.html Note: 1, Linux Shell 按Tab键不能补全 发现使用新增的用户登陆的 ...

  8. [android] 本地推送服务

    遇到新需求:游戏要添加本地的推送功能,ios比较好搞,在应用退出时的系统回调中设置,android就稍稍麻烦一点,需要用到 android中的service,和receiver

  9. RIP协议两个版本对不连续子网的支持情况实验

    一.连续子网与不连续子网 我们经常见到说RIPv1不支持不连续子网,仅支持连续子网,那么什么是连续子网,什么是不连续子网呢? l  不连续子网:指在一个网络中,某几个连续由同一主网划分的子网在中间被多 ...

  10. 解决eclipse无法打开:Failed to load the JNI shared library

    64位系统安装了64位的eclipse,但是jdk是32位的 总之就是eclipse跟jdk搭配不上,所以解决的时候注意这一点