Why can I “fake” the stack trace of an exception in Java?

后端 未结 6 849
梦如初夏
梦如初夏 2021-02-19 10:39

If I run the following test, it fails:

public class CrazyExceptions {
    private Exception exception;

    @Before
    public void setUp(){
        exception =          


        
相关标签:
6条回答
  • 2021-02-19 11:14

    Because you didn't ask that that stack trace be rewritten. It was set when you created it in the setUp method, and you never did anything to alter it.

    The Exception class doesn't give you any opportunity to set the method name; it's immutable. So there's no way that I know of where you could re-set the method name, unless you wanted to resort to something heinous like reflection.

    Your @Test annotation doesn't tell me if you're using JUnit or TestNG, because I can't see the static import, but in either case you can run a test to see if a particular exception is thrown by using the "expected" member in the @Test annotation.

    0 讨论(0)
  • 2021-02-19 11:17

    The stacktrace of the exception is filled in at creation time of the exception. Otherwise it would be impossible to catch an exception, handle it and rethrow it. The original stacktrace would get lost.

    If you want to force this you have to call exception.fillInStackTrace() explicitly.

    0 讨论(0)
  • 2021-02-19 11:18

    The stack trace is created when the exception is instantiated, not when it is thrown. This is specified behaviour of the 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. 
    

    I don't know why they did it that way, but if the specification defines it like that, it is at least consistent on all the various Java VMs.

    However, you can refresh it by calling exception.fillInStackTrace() manually.

    Also note that you should use Thread.currentThread().getStackTrace() instead of using new Exception().getStackTrace() (bad style).

    0 讨论(0)
  • 2021-02-19 11:19

    The stack trace in the exception corresponds to the "new" operation, nothing else.

    0 讨论(0)
  • 2021-02-19 11:26

    I think the assumption is that you won't be instantiating an exception unless you are in the process of throwing it, so why pay the price to get the stack trace twice?

    It would be difficult to recreate the stack trace while throwing it, as that is just sending the object out.

    The exception should be fully setup before the throw, so part of the instantiation is to get the stack trace.

    UPDATE:

    You can call fillInStackTrace() to resolve this.

    0 讨论(0)
  • 2021-02-19 11:34

    You wouldn't want throwing an exception to alter the stack track or you couldn't re-throw an exception safely.

    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.
        }
    }
    
    0 讨论(0)
提交回复
热议问题