Зачем ловить исключения в Java, когда вы можете поймать Throwables?



недавно у нас была проблема с серверным приложением Java, где приложение бросало ошибки, которые не были пойманы, потому что ошибка-это отдельный подкласс Throwable, и мы ловили только исключения.



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



и С чего бы вы хотите поймать исключения, когда вы можете поймать Throwables?

338   14  

14 ответов:

все зависит от того, что вы собираетесь делать с ошибкой, как только вы поймали его. В общем, перехват ошибок, вероятно, не следует рассматривать как часть вашего "нормального" потока исключений. Если вы поймаете его, вы не должны думать о "продолжении, как будто ничего не произошло", потому что JVM (и различные библиотеки) будут использовать ошибки как способ сигнализировать о том, что "что-то действительно серьезное произошло, и нам нужно как можно скорее закрыть". В общем, лучше всего слушать их, когда они говорят вам, что конец близко.

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

тем не менее, есть несколько угловых случаев, когда безопасно и/или желательно ловить ошибки или, по крайней мере, некоторые подклассы:

  • есть случаи, когда вы действительно хотите остановить нормальный ход потока: например, если вы находитесь в сервлете, возможно, вы не хотите, чтобы обработчик исключений по умолчанию Servlet runner объявил миру, что у вас был OutOfMemoryError, независимо от того, можете ли вы восстановить его.
  • иногда возникает ошибка в тех случаях, когда JVM может полностью восстановить причину ошибки. Например, если OutOfMemoryError возникает при попытке выделить массив, по крайней мере, в Hotspot, кажется, вы можете безопасно восстановиться от этого. (Есть, конечно, и другие случаи, когда OutOfMemoryError может быть брошен туда, где не безопасно попробовать и пахать.)

Итак, суть в следующем: если вы поймаете Throwable / Error, а не исключение, это должно быть четко определенный случай, когда вы знаете, что вы "делаете что-то особенное".

Edit: возможно, это очевидно, но я забыл сказать, что на практике,JVM может фактически не вызывать ваше предложение catch по ошибке. Я определенно видел, как Hotspot Бойко замалчивает попытки поймать некоторые OutOfMemoryErrors и NoClassDefFoundError.

из документации Java API:

класс Exception и его подклассы являются формой Throwable это указывает на условия, которые разумное приложение может захотеть поймать.

An Error является наследником Throwable это указывает на серьезные проблемы, которые разумное приложение не должно пытаться поймать.

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

обычно ошибки-это проблемы, которые вы не можете восстановить, например OutOfMemoryError. Там нет ничего, чтобы сделать, поймав их, так что вы обычно должны позволить им бежать, и сбить виртуальную машину.

многие другие ответы смотрят на вещи слишком узко.

Как говорится, если вы пишете код приложения, вы не должны поймать Throwable. Вы ничего не можете с этим поделать, поэтому лучше всего позволить окружающей системе (JVM или framework) справиться с этими проблемами.

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

Я пойду немного другим путем от других.

есть много случаев, когда вы хотели бы поймать Throwable (в основном, чтобы войти/сообщить, что произошло что-то злое).

, вы должны быть осторожны и переосмыслить все, что вы не можете иметь дело с.

это особенно верно для ThreadDeath.

Если вы когда-нибудь поймать Throwable, обязательно сделайте следующее:

try {
    ...
} catch (SomeExceptionYouCanDoSomethingWith e) {
    // handle it
} catch (ThreadDeath t) {
    throw t;
} catch (Throwable t) {
    // log & rethrow
}

не когда-нибудь лови Throwable или Error и вы, как правило, не должны просто поймать общий Exception либо. Errors-это, как правило, вещи,которые большинство разумных программ не могут восстановить. Если вы знаете, что происходит, вы можете быть в состоянии восстановить от одной конкретной ошибки, но в этом случае, вы должны поймать только что одна конкретная ошибка, а не все ошибки в целом.

хорошая причина, чтобы не поймать Error из-за ThreadDeath. ThreadDeath это довольно нормальное явление, которое теоретически может быть выброшено из любого места (другие процессы, такие как сам JVM, могут его генерировать), и весь смысл в том, чтобы убить ваш поток. ThreadDeath явно Error, а не Exception потому что слишком много людей поймать все Exception s. Если вы когда-нибудь должны были поймать ThreadDeath, вы должны перестроить его так, что ваш поток на самом деле умирает.

если у вас есть контроль над источником он, вероятно, должен быть реструктурирован, чтобы бросить Exception, а не Error. Если вы этого не сделаете, вам, вероятно, следует позвонить поставщику и пожаловаться. Errors должны быть зарезервированы только для вещей, которые являются терминальными без возможности восстановления из них.

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


public void run() {
   try {
       ...
   }
   catch(Throwable t) {
       threadCompletionError = t;
   }
}

Я действительно не уверен, что это лучший подход, но он работает. И у меня была ошибка "ClassNotFound", вызванная JVM, и это ошибка, а не ошибка исключение. Если я позволю исключение быть брошенным, я не уверен, как поймать его в вызывающем потоке (вероятно, есть метод, но я не знаю об этом - пока).

Что касается метода ThreadDeath, не вызывайте "Thread.стоп()" метод. Вызовите Поток.и прервать ваш поток, чтобы проверить, если он был прерван кем-то.

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

try {
    ...
} catch(Exception e) {
    ...
}

это ловит все ошибки, которые могут быть восстановлены и все те, которые указывают на ошибку в вашем коде, например InvalidArgumentException,NullPointerException. Затем вы можете автоматически отправить электронное письмо, отобразить окно сообщения или что угодно, так как сам JavaVM все еще работает нормально.

все производные от Error is что-то очень плохое, вы ничего не можете сделать против. Вопрос в том, имеет ли смысл ловить OutOfMemoryError или VirtualMachineError. (Это ошибка в самом JavaVM, вероятно, вы даже не можете отобразить окно сообщения или отправить электронное письмо)

вы, вероятно, не должны класс, производный от Error, вы должны наследовать от Exception или RuntimeException.

Я знаю, что это может быть контр-интуитивным, но только потому, что вы можете поймать все виды исключений и Throwables и ошибок делает не означает, что вы должны.

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

Лучшая практика: только поймать

  1. исключения, которые можно обработать
  2. исключения, которые необходимо поймать

В общем, было бы разумно попытаться поймать ошибки, если только это может быть правильно сообщено.

тем не менее, я считаю, что есть случаи, когда было бы целесообразно поймать ошибку и не сообщать об этом. Я имею в виду UnsatisfiedLinkError. В JAI библиотека использует некоторые собственные библиотеки для реализации большинства операторов по соображениям производительности, однако если библиотека не загружается (не существует, неправильный формат, неподдерживаемая платформа), библиотека все равно будет функционировать потому что он вернется в режим только java.

этот пост не сделает "проверенные исключения плохие" люди счастливы. Однако я основываю свой ответ на том, как исключения Java предназначены для использования, как определено людьми, которые создали язык.

краткая справочная таблица:

  • Throwable-никогда не поймать это
  • ошибка-указывает на ошибку VM-никогда не поймать это
  • RuntimeException-указал на ошибку программиста-никогда не поймать это
  • исключение - никогда не поймать это

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

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

есть исключения (без каламбура) для вышеуказанных "правил":

  • код, с которым вы работаете (от третьей стороны) бросает Throwable или Exception
  • вы работаете ненадежным код, который может привести к сбою вашей программы, если это исключение.

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

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

Почему бы не поймать их всех? Тогда регистрируйте их, по крайней мере, вы знаете, что у вас есть ошибка. Так что лучше поймать Throwable/s, чем Exception / s только.

нет смысла ловить .

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

например, одна распространенная ошибка

java.lang.OutOfMemoryError

здесь ничего вы можете сделать, когда это произойдет. Уже слишком поздно, JVM исчерпал все свои возможности, чтобы получить больше памяти, но это невозможно.

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

Comments

    Ничего не найдено.