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

R. *_*des 13 java callstack exception

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

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());
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

出现以下错误:

Expected :stackTraceMentionsTheLocationWhereTheExceptionWasThrown
Actual   :setUp
Run Code Online (Sandbox Code Playgroud)

堆栈跟踪只是平坦的说谎.

抛出异常时,为什么不重写堆栈跟踪?我不是Java开发者,也许我在这里遗漏了一些东西.

mha*_*ler 20

实例化异常时创建堆栈跟踪,而不是在抛出异常时创建堆栈跟踪.这是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. 
Run Code Online (Sandbox Code Playgroud)

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

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

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

  • @mhaller,没有`[1]`没有得到JDK 1.5中的当前方法名称,它是[2](有一个额外的内部方法调用).所以getStackTrace非常适合查看堆栈,如果你关心你的位置很糟糕(谁说它不会在另一个版本中改为3或者回到1). (3认同)

tan*_*ens 10

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

如果你想强制这个,你必须exception.fillInStackTrace()明确调用.

  • @ 280Z28,问题不在于通过改变语言来解决它,而是为什么java在抛出时没有设置堆栈跟踪.它不是因为它没有重新抛出和保留旧堆栈跟踪的机制.相反,如果您需要重置堆栈跟踪,它有fillInStackTrace.这是一个不同的设计. (2认同)