14

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

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 开发人员,也许我在这里遗漏了一些东西。

4

6 回答 6

21

堆栈跟踪是在实例化异常时创建的,而不是在抛出异常时创建的。这是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()(不良风格)。

于 2009-11-08T14:59:41.420 回答
10

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

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

于 2009-11-08T15:03:04.800 回答
1

因为您没有要求重写该堆栈跟踪。它是在您在 setUp 方法中创建它时设置的,并且您从未对它进行任何更改。

Exception 类没有给您任何设置方法名称的机会;它是不可变的。所以我不知道你可以在哪里重新设置方法名称,除非你想求助于像反射这样令人发指的东西。

您的 @Test 注释没有告诉我您使用的是 JUnit 还是 TestNG,因为我看不到静态导入,但在任何一种情况下,您都可以运行测试以查看是否使用“预期" @Test 注释中的成员。

于 2009-11-08T14:58:44.393 回答
1

您不希望抛出异常来改变堆栈轨道,或者您无法安全地重新抛出异常。

public void throwsException() {
    throw new RuntimeException();
}

public void logsException() {
    try {
        throwsException();
    } catch (RuntimeException e) {
        e.printStrackTrace();
        throw e; // doesn't alter the exception.
    }
}

@Test
public void youCanSeeTheCauseOfAnException(){
    try {
        logsException();
    } catch(Exception e) {
        e.printStrackTrace(); // shows you the case of the exception, not where it was last re-thrown.
    }
}
于 2009-11-08T16:19:35.807 回答
0

我认为假设是除非您正在抛出异常,否则您不会实例化异常,那么为什么要付出代价来获得两次堆栈跟踪呢?

在抛出堆栈跟踪时很难重新创建它,因为这只是将对象发送出去。

异常应该在抛出之前完全设置,因此部分实例化是获取堆栈跟踪。

更新:

你可以打电话fillInStackTrace()来解决这个问题。

于 2009-11-08T14:59:29.377 回答
0

异常中的堆栈跟踪对应于“新建”操作,仅此而已。

于 2009-11-08T20:18:27.487 回答