1. 概述

作为一名Java开发者,在处理异常时,遇到堆栈跟踪的概念非常常见。

在这个教程中,我们将理解什么是堆栈跟踪,以及在编程和调试过程中如何使用它。此外,我们还将深入研究StackTraceElement类。最后,我们将学习如何通过ThreadThrowable类获取堆栈跟踪。

2. 什么是堆栈跟踪?

堆栈跟踪,也称为回溯,是一系列堆栈帧的列表。简单地说,这些帧代表程序执行过程中的某个时刻。

一个堆栈帧包含了代码调用的方法的相关信息。它是一个从当前方法开始,延伸到程序启动时的帧列表。

为了更好地理解,让我们看一个例子,当一个异常被抛出后,我们在其中dump了当前堆栈跟踪:

public class DumpStackTraceDemo 
{ 
    public static void main(String[] args) {
        methodA(); 
    } 

    public static void methodA() {
        try {
            int num1 = 5/0; // java.lang.ArithmeticException: divide by zero
        }
        catch (Exception e) {
            e.printStackTrace();
        }
    }
}

在上面的例子中,methodA()抛出了ArithmeticException,这会导致在catch块中打印当前堆栈跟踪:

java.lang.ArithmeticException: / by zero
at main.java.com.baeldung.tutorials.DumpStackTraceDemo.methodA(DumpStackTraceDemo.java:11)
at main.java.com.baeldung.tutorials.DumpStackTraceDemo.main(DumpStackTraceDemo.java:6)

3. StackTraceElement

堆栈跟踪由StackTraceElement类的元素组成。我们可以使用以下方法获取类名和方法名,分别如下:

  • getClassName() - 返回包含当前执行点的完整类名。
  • getMethodName() - 返回包含由这个堆栈跟踪元素表示的执行点的方法名称。

Java API文档中,可以查看StackTraceElement类中完整的方法列表及其详细信息。

4. 使用Thread类获取堆栈跟踪

我们可以通过在Thread实例上调用getStackTrace()方法来从线程中获取堆栈跟踪。它返回一个StackTraceElement数组,从中可以找到线程的堆栈帧详细信息。

让我们看一个例子:

public class StackTraceUsingThreadDemo {

    public static void main(String[] args) {
        methodA();
    }
    
    public static StackTraceElement[] methodA() {
        return methodB();
    }

    public static StackTraceElement[] methodB() {
        Thread thread = Thread.currentThread();
        return thread.getStackTrace();
    }
}

在上述类中,方法调用顺序如下:main() -> methodA() -> methodB() -> getStackTrace()

让我们通过以下测试用例来验证这一点,其中测试用例方法调用了methodA()

@Test
public void whenElementIsFetchedUsingThread_thenCorrectMethodAndClassIsReturned() {
    StackTraceElement[] stackTrace = new StackTraceUsingThreadDemo().methodA();
    
    StackTraceElement elementZero = stackTrace[0];
    assertEquals("java.lang.Thread", elementZero.getClassName());
    assertEquals("getStackTrace", elementZero.getMethodName());
    
    StackTraceElement elementOne = stackTrace[1];
    assertEquals("com.baeldung.tutorials.StackTraceUsingThreadDemo", elementOne.getClassName());
    assertEquals("methodB", elementOne.getMethodName());
    
    StackTraceElement elementTwo = stackTrace[2];
    assertEquals("com.baeldung.tutorials.StackTraceUsingThreadDemo", elementTwo.getClassName());
    assertEquals("methodA", elementTwo.getMethodName());
    
    StackTraceElement elementThree = stackTrace[3];
    assertEquals("test.java.com.baeldung.tutorials.CurrentStacktraceDemoUnitTest", elementThree.getClassName());
    assertEquals("whenElementIsFetchedUsingThread_thenCorrectMethodAndClassIsReturned", elementThree.getMethodName());
}

在上述测试用例中,我们通过StackTraceUsingThreadDemo类的methodB()方法获取了一个StackTraceElement数组。然后,我们使用StackTraceElement类的getClassName()getMethodName()方法验证方法和类名。

5. 使用Throwable类获取堆栈跟踪

当Java程序抛出一个Throwable对象时,我们不仅可以直接在控制台或日志中打印它,还可以通过调用getStackTrace()方法获取一个StackTraceElement对象数组。

让我们看一个例子:

public class StackTraceUsingThrowableDemo {
    
    public static void main(String[] args) {
        methodA(); 
    } 

    public static StackTraceElement[] methodA() {
        try {
            methodB();
        } catch (Throwable t) {
            return t.getStackTrace();
        }
        return null;
    }

    public static void methodB() throws Throwable {
        throw new Throwable("A test exception");
    }
}

这里的方法调用顺序如下:main() -> methodA() -> methodB() -> getStackTrace()

让我们通过测试来验证这一点:

@Test
public void whenElementIsFecthedUsingThrowable_thenCorrectMethodAndClassIsReturned() {
    StackTraceElement[] stackTrace = new StackTraceUsingThrowableDemo().methodA();

    StackTraceElement elementZero = stackTrace[0];
    assertEquals("com.baeldung.tutorials.StackTraceUsingThrowableDemo", elementZero.getClassName());
    assertEquals("methodB", elementZero.getMethodName());

    StackTraceElement elementOne = stackTrace[1];
    assertEquals("com.baeldung.tutorials.StackTraceUsingThrowableDemo", elementOne.getClassName());
    assertEquals("methodA", elementOne.getMethodName());

    StackTraceElement elementThree = stackTrace[2];
    assertEquals("test.java.com.baeldung.tutorials.CurrentStacktraceDemoUnitTest", elementThree.getClassName());
    assertEquals("whenElementIsFecthedUsingThrowable_thenCorrectMethodAndClassIsReturned", elementThree.getMethodName());
}

在上述测试用例中,我们通过StackTraceUsingThrowableDemo类的methodB()方法获取了一个StackTraceElement数组。然后,我们检查方法和类名,以了解StackTraceElement数组中元素的顺序。

6. 总结

在这篇文章中,我们学习了Java堆栈跟踪以及如何使用printStackTrace()方法在遇到异常时打印它。我们还了解了如何通过ThreadThrowable类获取当前堆栈跟踪。

如往常一样,本文的完整代码示例可在GitHub上找到。