为什么我可以“伪造”Java中异常的堆栈跟踪?

2022-09-03 17:48:22

如果我运行以下测试,它将失败:

public class CrazyExceptions {
    private Exception exception;

    @Before
    public void setUp(){
        exception = new Exception();
    }

    @Test
    public void stackTraceMentionsTheLocationWhereTheExceptionWasThrown(){
        String thisMethod = new Exception().getStackTrace()[0].getMethodName();
        try {
            throw exception;
        }
        catch(Exception e) {
            assertEquals(thisMethod, e.getStackTrace()[0].getMethodName());
        }
    }
}

出现以下错误:

Expected :stackTraceMentionsTheLocationWhereTheExceptionWasThrown
Actual   :setUp

堆栈跟踪只是平坦地躺着。

为什么在引发异常时不重写堆栈跟踪?我不是Java开发人员,也许我在这里错过了一些东西。


答案 1

堆栈跟踪是在实例化异常时创建的,而不是在引发异常时创建的。这是 Java 语言规范的指定行为

20.22.1  public Throwable()

This constructor initializes a newly created Throwable object with null as
its error message string. Also, the method fillInStackTrace (§20.22.5) is
called for this object. 

....

20.22.5  public Throwable fillInStackTrace()

This method records within this Throwable object information about the
current state of the stack frames for the current thread. 

我不知道他们为什么这样做,但是如果规范这样定义它,它至少在所有各种Java VM上都是一致的。

但是,您可以通过手动调用来刷新它。exception.fillInStackTrace()

另请注意,您应该使用而不是使用(不良样式)。Thread.currentThread().getStackTrace()new Exception().getStackTrace()


答案 2

异常的堆栈跟踪在异常的创建时填充。否则,就不可能捕获异常,处理它并重新删除它。原始堆栈跟踪将丢失。

如果要强制执行此操作,则必须显式调用。exception.fillInStackTrace()