Подтвердить что ты не робот

Почему я могу "подделать" трассировку стека исключения в Java?

Если я запускаю следующий тест, он терпит неудачу:

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-разработчик, и, возможно, мне что-то не хватает.

4b9b3361

Ответ 1

Трассировка стека создается, когда создается исключение, а не когда оно выбрано. Это указано поведение Спецификация языка 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.

Однако вы можете обновить его, вызвав exception.fillInStackTrace() вручную.

Также обратите внимание, что вместо new Exception().getStackTrace() (плохой стиль) следует использовать Thread.currentThread().getStackTrace().

Ответ 2

Стоп-код исключения заполняется во время создания исключения. В противном случае невозможно было бы поймать исключение, обработать его и реконструировать. Исходная stacktrace будет потеряна.

Если вы хотите принудительно выполнить это, вы должны явно вызвать exception.fillInStackTrace().

Ответ 3

Потому что вы не просили переписать эту трассировку стека. Он был установлен, когда вы создали его в методе setUp, и вы ничего не сделали, чтобы его изменить.

Класс Exception не дает вам возможности установить имя метода; он непреложный. Поэтому я не знаю, где вы могли бы переустановить имя метода, если только вы не хотели прибегнуть к чему-то отвратительному, как отражение.

Аннота @Test не говорит мне, используете ли вы JUnit или TestNG, потому что я не вижу статический импорт, но в любом случае вы можете запустить тест, чтобы узнать, выбрано ли какое-либо конкретное исключение, используя "ожидаемый" член в аннотации @Test.

Ответ 4

Вы не хотели бы бросать исключение для изменения дорожки стека, иначе вы не смогли бы безопасно перебросить исключение.

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

Я думаю, что предположение состоит в том, что вы не будете создавать исключение, если только вы не создаете его, так зачем платить цену, чтобы получить трассировку стека дважды?

Было бы трудно воссоздать трассировку стека во время его выброса, поскольку это всего лишь отправка объекта.

Исключение должно быть полностью настроено перед броском, поэтому частью экземпляра является получение трассировки стека.

ОБНОВИТЬ:

Вы можете вызвать fillInStackTrace() чтобы решить эту проблему.

Ответ 6

Трассировка стека в исключении соответствует "новой" операции, ничего больше.