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

“为什么我可以?”;假;Java中异常的堆栈跟踪?,java,exception,callstack,Java,Exception,Callstack,如果我运行以下测试,它将失败: public class CrazyExceptions { private Exception exception; @Before public void setUp(){ exception = new Exception(); } @Test public void stackTraceMentionsTheLocationWhereTheExceptionWasThrown(){

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

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

因为您没有要求重写堆栈跟踪。它是在您使用setUp方法创建时设置的,您从未做过任何更改

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


您的@Test注释没有告诉我您使用的是JUnit还是TestNG,因为我看不到静态导入,但是在这两种情况下,您都可以运行一个测试,通过使用“预期”来查看是否引发了特定的异常@Test注释中的成员。

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

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

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

更新:


您可以调用来解决此问题。

堆栈跟踪是在实例化异常时创建的,而不是在抛出异常时创建的。这是系统的特定行为

我不知道他们为什么这样做,但是如果规范这样定义它,那么它至少在所有不同的Java虚拟机上是一致的

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


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

在创建异常时填写异常的stacktrace。否则就不可能捕获异常、处理它并重新播放它。原来的stacktrace会丢失


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

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

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.
    }
}

异常中的堆栈跟踪与“new”操作相对应,没有其他内容。

我正在使用jUnit,但我没有尝试测试抛出的异常。我使用该测试只是为了说明我的问题。我不确定问题是什么:“如何从堆栈跟踪更改方法?”我没有使用
Thread.currentThread().getStackTrace()
,因为
Thread.currentThread().getStackTrace()[0].getMethodName
始终是
getStacktrace
…+1,用于指出手册
fillInStackTrace
内容。在C#中,我们默认得到该行为,我们可以简单地
抛出当我们在重试时需要保留堆栈跟踪时。它是
getStackTrace()[1]
,仅此而已。@mhaller,没有
[1]
在JDK 1.5中无法获取当前方法名,它是[2](有一个额外的内部方法调用)。因此,getStackTrace很适合查看堆栈,如果你关心自己在哪里(谁说它不会在另一个版本中更改为3或返回到1),那就糟糕了当我们需要在重新刷新时保留堆栈跟踪时。这就是为什么我感到困惑。-1:我们从.NET知道情况并非如此。当捕获堆栈跟踪时,当然有一些基本原理,但这并不是因为当被
throw e捕获时无法对其进行管理语句。@280Z28,关键是在java中是没有办法的,因为它没有像throw;这样的语言结构@义海:是的,但是请记住我们谈论的是语言设计本身,所以如果这是问题所在,他们可以绕过它。他们可能会称之为
rethrow
,但不管它变成什么,显然他们需要能够在不更改堆栈跟踪的情况下重新刷新异常。@280Z28,问题不是通过更改语言有什么办法可以解决这个问题,而是为什么java在抛出堆栈跟踪时不设置堆栈跟踪。这不是因为它没有机制来重新排序和保留旧堆栈跟踪。相反,如果需要重置堆栈跟踪,它具有fillInStackTrace。这是一种不同的设计。我不知道6年前我们是否可以设置异常的堆栈跟踪“原因”,但最好在创建新异常后并在抛出它之前使用它:exception.initCause(Throwable),其中您可以使用setStackTrace()配置原因堆栈跟踪。
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.
    }
}