Зачем ловить исключения в Java, когда вы можете ловить Throwables? - PullRequest
59 голосов
/ 24 февраля 2009

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

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

Итак, зачем вам ловить исключения, когда вы можете ловить Throwables ?

Ответы [ 14 ]

58 голосов
/ 24 февраля 2009

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

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

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

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

57 голосов
/ 24 февраля 2009

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

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

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

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

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

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

6 голосов
/ 24 февраля 2009

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

5 голосов
/ 24 февраля 2009

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

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

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

4 голосов
/ 24 февраля 2009

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

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

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

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

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

try {
    ...
} catch (SomeExceptionYouCanDoSomethingWith e) {
    // handle it
} catch (ThreadDeath t) {
    throw t;
} catch (Throwable t) {
    // log & rethrow
}
2 голосов
/ 24 февраля 2009

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


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

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

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

2 голосов
/ 24 февраля 2009

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

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

  • Бросай - никогда не лови это
  • Ошибка - указывает на ошибку ВМ - никогда не перехватывать это
  • RuntimeException - указывает на ошибку программиста - никогда не ловить это
  • Исключение - никогда не поймать это

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

Причина, по которой вы не должны перехватывать Throwable, заключается в том, что он перехватывает все подклассы, включая Error и Exception.

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

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

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

2 голосов
/ 24 февраля 2009

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

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

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

1 голос
/ 24 февраля 2009

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

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

1 голос
/ 24 февраля 2009

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

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

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

  1. Исключения, которые вы можете обработать
  2. Исключения, которые необходимо отловить
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...