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

Почему метод Throwable.fillInStackTrace() общедоступен? Зачем кому-то его использовать?

Мне любопытно, почему метод fillInStackTrace of java.lang.Throwable public?

Этот метод заменяет исходную трассировку стека на то, что из вызываемого места, удаляя информацию, необходимую для локализации исключения. Его можно использовать для обфускации, но без особых усилий, поскольку новая трассировка стека будет направлять на обфускационный код. Лучше всего было бы просто скрыть исключение или выбросить новый.

Но я не могу найти случай разумный для вызова этого метода на существующий Throwable. Поэтому возникает вопрос: почему этот метод является общедоступным? Есть ли смысл?

4b9b3361

Ответ 1

Одна из причин заключается в производительности. Бросок и ловля исключения - это дешево; дорогая часть заполняет трассировку стека. Если вы переопределите fillInStackTrace(), чтобы ничего не делать, создание исключения также становится дешевым.

С дешевыми исключениями вы можете использовать исключения для управления потоком, которые могут сделать код более читаемым в определенных ситуациях; вы можете использовать их, когда при внедрении JVM-языков, где вам нужно более продвинутое управление потоком, и они полезны, если вы пишете библиотека участников.

Ответ 2

Одним из возможных законных способов использования является создание исключения в другом месте, откуда вы на самом деле бросаете. Например, возможно, у вас есть приложение, в котором вы предоставляете средство плагина для генерации пользовательских исключений. Поскольку stacktrace заполняется при построении, след исключения будет включать в себя возможную вводящую в заблуждение информацию (он будет включать вызовы в пользовательское исключение factory). таким образом, ваше приложение будет вызывать fillInStackTrace() в исключении после генерации пользовательским factory, чтобы дать более точную трассировку стека для конечного получателя исключения.

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

Ответ 3

Один законный вариант использования Throwable.fillInStackTrace() предназначен для нелокального потока управления:

Например, в рамках TrueZIP я использую сложную цепочку декораторов для контроллеров файловой системы. Один из контроллеров в этой цепочке является экземпляром класса FsLockController. Этот объект отвечает за управление ReentrantReadWriteLock для файловой системы. Теперь контроллер файловой системы где-то глубже в этой цепочке может обнаружить, что требуется блокировка записи, но FcLockController получил только блокировку чтения. Поскольку вы не можете обновить блокировку чтения до блокировки записи без блокировки, необходимо исключить исключение, которое, случается, является экземпляром класса FsNeedsWriteLockException. Затем украшающий FsLockController поймает это исключение, освободит блокировку чтения и приобретет блокировку записи, прежде чем снова повторить операцию.

Этот вид нелокального потока управления работает на самом деле очень хорошо, но есть одна вещь, которую следует учитывать: бросать и ловить исключение дешево, но заполнение или проверка его трассировки стека является дорогостоящим. Теперь, поскольку этот конкретный тип исключения вызывается только и попадает в эту цепочку контроллера файловой системы, мне вообще не нужна трассировка стека, поэтому я могу безопасно переопределить Throwable.fillInStackTrace() пустым телом метода, чтобы подавить эту дорогостоящую операцию.

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

Ответ 4

Вот один разумный случай: скрыть детали вашего приложения от неавторизованного пользователя. Например, вы не хотите выставлять трассировку стека исключений, возникающих при истечении срока действия ключа лицензии, поскольку это упростит работу хакера.

Ответ 5

Аналогичный question дает ответ.

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

Чтобы разрешить эту функциональность, у нас есть fillInStackTrace()

ИЗМЕНИТЬ

Хорошо, сегодня я узнал, что спецификация стандартной библиотеки Java была удалена из JLS после ревизии 1. В предисловии ко второму изданию говорится:

The specifications of the libraries are now far too large to fit into this volume, and
they continue to evolve. Consequently, API specifications have been removed from
this book. The library specifications can be found on the Web; this specification
now concentrates solely on the Java programming language proper.

Таким образом, нам действительно нужно более внимательно изучить javadoc, чтобы понять это. API здесь неожиданно свободен. Все соответствующие части принадлежат заголовку, который гласит:

Instances of two subclasses, Error and Exception, are conventionally used to indicate   
that exceptional situations have occurred. Typically, these instances are freshly 
created in the context of the exceptional situation so as to include relevant 
information (such as stack trace data).

И java.lang.Throwable.printStackTrace() -

Prints this throwable and its backtrace to the standard error stream.

Я убежден, что это должно позволить разработчикам VM предоставлять все, что является наиболее эффективной/подходящей реализацией для их контекста. С этим контекстом наличие явного fillInStackTrace() public делает еще больше смысла.

По умолчанию виртуальные машины предоставляют вам наилучшее местоположение backtrace. Создание fillInStackTrace() public позволяет разработчикам дополнительно уточнять.

-

последнее редактирование:)

Мне удалось найти оставшуюся ссылку на соответствующие биты JLS First Edition, и я хотел бы предложить еще одну причину почему метод является общедоступным. Это для лучшего или худшего общего объяснения в Java.

Это всегда было так.

Ответ 6

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

Это также будет работать, если у вас есть одноэлементное исключение, созданное один раз, например:

private static Exception theException = new MyException();

который вызывается несколько раз. Это установило бы stackTrack на что-то, связанное с пойманным исключением, без необходимости дорогого создания нового исключения каждый раз. Я думаю, что это как Scala выполняет свои функции продолжения и разрыва.

try {
    ....
} catch (OtherException oe) {
    theException.fillStackTrace(oe.getStackTrace());
    theException.setMessage(...); //etc
    throw theException;
}

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