Исключение без трассировки стека в Java

Это, вероятно, очень наивный вопрос.

Раньше я полагал, что Throwable в Java всегда содержит трассировку стека. Правильно ли это? Теперь похоже, что я улавливаю exceptions без трассировки стека. Имеет ли это смысл? Можно ли исключить исключение без трассировки стека?

Ответ 1

Можно поймать Throwable object в Java без трассировки стека:

Throwable(String message, Throwable cause, boolean enableSuppression,boolean writableStackTrace) 

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

http://docs.oracle.com/javase/7/docs/api/java/lang/Throwable.html

Ответ 2

Для Java 6:

Поскольку Java 6 не имеет конструктора Throwable(String message, Throwable cause, boolean enableSuppression,boolean writableStackTrace), мы можем подавить заполнение stacktrace, используя нижеприведенную технику (заимствованную из Scala, узнать из Насколько медленны Java исключения?)

class NoStackTraceRuntimeException extends RuntimeException {
    @Override
    public synchronized Throwable fillInStackTrace() {
        return this;
    }
}

Использование такое же: throw new NoStackTraceRuntimeException (), или это подтипы.

Мы также можем сделать то же самое, расширяя Throwable:

class NoStackTraceThrowable extends Throwable {
    @Override
    public synchronized Throwable fillInStackTrace() {
        return this;
    }
}

Но небольшая уловка заключается в том, что вы больше не можете catch использовать это исключение, используя Exception, поскольку это не подтип Exception, вместо этого следует поймать NoStackTraceThrowable или его подтипы.

Обновить. Для некоторых интересных статистических данных о производительности в разных условиях использования проверьте этот вопрос SO

Ответ 3

Для Java 7+ здесь приведен пример исключения, в котором трассировка стека может быть отключена.

public class SuppressableStacktraceException extends Exception {

    private boolean suppressStacktrace = false;

    public SuppressableStacktraceException(String message, boolean suppressStacktrace) {
        super(message, null, suppressStacktrace, !suppressStacktrace);
        this.suppressStacktrace = suppressStacktrace;
    }

    @Override
    public String toString() {
        if (suppressStacktrace) {
            return getLocalizedMessage();
        } else {
            return super.toString();
        }
    }
}

Это можно продемонстрировать с помощью:

try {
    throw new SuppressableStacktraceException("Not suppressed", false);
} catch (SuppressableStacktraceException e) {
    e.printStackTrace();
}
try {
    throw new SuppressableStacktraceException("Suppressed", true);
} catch (SuppressableStacktraceException e) {
    e.printStackTrace();
}

Это основано на MLContextException из Apache SystemML, код которого доступен в GitHub at https://github.com/apache/systemml.

Ответ 4

Самый простой способ подавить stacktrace для любого исключения -

throwable.setStackTrace(new StackTraceElement[0]);

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

Это также уменьшает дорогостоящее создание трассировки стека как можно больше

Элемент stacktrace для throwable инициализируется в

Throwable#fillInStackTrace()

который вызывается любым конструктором и, следовательно, его нельзя избежать. Когда на самом деле используется stacktrace, StackTraceElement [] лениво построен в

Throwable#getOurStackTrace()

что происходит, если поле Throwable.stackTrace еще не установлено.

Устанавливая stacktrace на любое ненулевое значение, избегает построения StackTraceElement [] в Throwable # getOurStackTrace() и как можно меньше снижает эффективность.