代码之家  ›  专栏  ›  技术社区  ›  R. Martinho Fernandes

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

  •  13
  • R. Martinho Fernandes  · 技术社区  · 15 年前

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

    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
    

    堆栈跟踪只是平躺在地上。

    当抛出异常时,为什么不重写堆栈跟踪?我不是爪哇开发者,也许我错过了一些东西。

    6 回复  |  直到 15 年前
        1
  •  20
  •   slim    15 年前

    堆栈跟踪是在实例化异常时创建的,而不是在引发异常时创建的。这是 Java Language Specification

    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虚拟机上都是一致的。

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

    还要注意,您应该使用 Thread.currentThread().getStackTrace() 而不是使用 new Exception().getStackTrace() (风格不好)。

        2
  •  10
  •   tangens    15 年前

    异常的StackTrace将在异常的创建时填写。否则就不可能 捕获异常,处理并重新引发 . 原来的stacktrace会丢失。

    如果你想强制这样做,你必须打电话 exception.fillInStackTrace() 明确地。

        3
  •  1
  •   duffymo    15 年前

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

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

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

        4
  •  1
  •   Peter Lawrey    15 年前

    您不希望抛出异常来更改堆栈跟踪,或者不能安全地重新抛出异常。

    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.
        }
    }
    
        5
  •  0
  •   Thorbjørn Ravn Andersen    15 年前

    异常中的堆栈跟踪与“new”操作相对应,而不是其他操作。

        6
  •  0
  •   Donal Fellows    6 年前

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

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

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

    更新:

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