Ответы:
Как правило, никогда.
Однако иногда нужно отловить конкретные ошибки.
Если вы пишете код, похожий на фреймворк (загрузка сторонних классов), было бы разумно поймать LinkageError
(определение класса не найдено, неудовлетворенная ссылка, несовместимое изменение класса).
Я также видел несколько глупых сторонних кодов, генерирующих подклассы Error
, так что вам тоже придется справиться с ними.
Кстати, я не уверен, что от этого невозможно вылечиться OutOfMemoryError
.
Никогда. Никогда нельзя быть уверенным, что приложение сможет выполнить следующую строку кода. Если вы его получите OutOfMemoryError
, у вас нет гарантии, что вы сможете сделать что-либо надежно . Поймать RuntimeException и проверить исключения, но не ошибки.
boolean assertionsEnabled = false; assert assertionsEnabled = true;
Как правило, вы всегда должны ловить java.lang.Error
и записывать это в журнал или отображать его пользователю. Я работаю в службе поддержки и ежедневно вижу, что программисты не могут сказать, что произошло в программе.
Если у вас есть поток демона, вы должны предотвратить его завершение. В остальных случаях ваше приложение будет работать правильно.
Ловить надо только java.lang.Error
на высшем уровне.
Если вы посмотрите на список ошибок, вы увидите, что с большинством из них можно справиться. Например, ошибка ZipError
возникает при чтении поврежденных zip-файлов.
Наиболее частыми ошибками являются OutOfMemoryError
и NoClassDefFoundError
, которые в большинстве случаев являются проблемами во время выполнения.
Например:
int length = Integer.parseInt(xyz);
byte[] buffer = new byte[length];
может вызвать ошибку, OutOfMemoryError
но это проблема времени выполнения, и нет причин для завершения вашей программы.
NoClassDefFoundError
чаще всего возникают, если библиотеки нет или вы работаете с другой версией Java. Если это необязательная часть вашей программы, вам не следует прекращать ее.
Я могу привести еще много примеров того, почему лучше поймать Throwable
на верхнем уровне и создать полезное сообщение об ошибке.
OutOfMemoryError
не является ошибкой во время выполнения, нет гарантии, что приложение сможет исправить ее. Если вам повезет, вы можете получить OOM, new byte[largeNumber]
но если этого выделения было недостаточно, чтобы вызвать OOM, оно могло быть запущено в следующей строке или следующем потоке. Это проблема времени выполнения, потому что если length
это ненадежный ввод, он должен быть проверен перед вызовом new byte[]
.
NoClassDefFoundError
может произойти где угодно , так как он вызывается, когда скомпилированный код Java не может найти класс. Если ваш JDK настроен неправильно, он может сработать при попытке использовать java.util.*
класс, и практически невозможно программировать против него. Если вы дополнительно включаете зависимость, вы должны использовать ее, ClassLoader
чтобы проверить, существует ли она, что вызывает ClassNotFoundException
.
ZipError
указывает, что файл jar, содержащий классы, является поврежденным файлом zip. Это довольно серьезная проблема, и на данный момент вы не можете доверять никакому исполняемому коду, и было бы безответственно пытаться «оправиться» от него.
java.lang.Error
или java.lang.Throwable
на верхнем уровне и попытаться что-то с ним сделать - например, записать сообщение об ошибке. Но на этом этапе нет гарантии, что это будет выполнено. Если ваша JVM работает с OOM, попытка регистрации может выделить больше String
s, что вызовет другой OOM.
В многопоточной среде его чаще всего хочется поймать! Когда вы поймаете это, зарегистрируйте его и завершите работу всего приложения! Если вы этого не сделаете, какой-то поток, который может выполнять какую-то важную часть, будет мертв, а остальная часть приложения будет думать, что все в порядке. Из-за этого может случиться много нежелательных ситуаций. Одна из самых маленьких проблем заключается в том, что вы не сможете легко найти корень проблемы, если другие потоки начнут генерировать некоторые исключения из-за того, что один поток не работает.
Например, обычно цикл должен быть:
try {
while (shouldRun()) {
doSomething();
}
}
catch (Throwable t) {
log(t);
stop();
System.exit(1);
}
Даже в некоторых случаях вам нужно обрабатывать разные ошибки по-разному, например, в OutOfMemoryError вы сможете регулярно закрывать приложение (даже, возможно, освободить некоторую память и продолжить), в некоторых других вы мало что можете сделать.
OutOfMemoryError
и продолжить, вместо того, чтобы сразу же существовать, неразумно, потому что ваша программа тогда находится в неопределенном состоянии .
Error
Обычно не должны быть пойманы , поскольку это указывает на ненормальное состояние , которое никогда не должно произойти .
Из спецификации Java API для Error
класса:
Это
Error
подкласс,Throwable
который указывает на серьезные проблемы, которые разумное приложение не должно пытаться уловить. Большинство таких ошибок являются ненормальными. [...]От метода не требуется объявлять в своем предложении throws какие-либо подклассы Error, которые могут быть сгенерированы во время выполнения метода, но не обнаружены, поскольку эти ошибки являются ненормальными условиями, которые никогда не должны возникать.
Как упоминается в спецификации, выдается Error
только в тех случаях, когда есть вероятность, что когда возникает Error
ошибка, приложение может сделать очень мало, а в некоторых случаях сама виртуальная машина Java может находиться в нестабильном состоянии (например, VirtualMachineError
)
Хотя an Error
является подклассом, Throwable
что означает, что он может быть перехвачен try-catch
предложением, но, вероятно, в этом нет необходимости, поскольку приложение будет в ненормальном состоянии, когда Error
JVM сгенерирует его.
Там также краткий раздел по этой теме в разделе 11.5 Иерархия исключений из спецификации языка Java, 2nd Edition .
И есть пара других случаев, когда, если вы поймаете ошибку, вам придется ее повторно выбросить . Например, ThreadDeath никогда не следует ловить, это может вызвать большую проблему, если вы поймаете его в изолированной среде (например, на сервере приложений):
Приложение должно перехватывать экземпляры этого класса только в том случае, если оно должно очистить после асинхронного завершения. Если ThreadDeath перехватывается методом, важно, чтобы он был запущен повторно, чтобы поток действительно умер.
Error
s.
Очень-очень редко.
Я сделал это только для одного очень известного случая. Например, java.lang.UnsatisfiedLinkError может быть сгенерирован, если два независимых ClassLoader загружают одну и ту же DLL. (Я согласен с тем, что мне следует переместить JAR в общий загрузчик классов)
Но наиболее распространенный случай - вам необходимо вести журнал, чтобы знать, что произошло, когда пользователь пришел с жалобой. Вам нужно сообщение или всплывающее окно для пользователя, а не молчание.
Даже программист на C / C ++ выдает сообщение об ошибке и сообщает что-то, чего люди не понимают, прежде чем она завершится (например, сбой памяти).
В приложении для Android я обнаруживаю ошибку java.lang.VerifyError . Библиотека, которую я использую, не будет работать на устройствах со старой версией ОС, и код библиотеки выдаст такую ошибку. Конечно, я мог бы избежать ошибки, проверив версию ОС во время выполнения, но:
В идеале мы не должны обрабатывать / отлавливать ошибки. Но могут быть случаи, когда нам нужно это сделать, исходя из требований фреймворка или приложения. Скажем, у меня есть демон XML Parser, который реализует DOM Parser, который потребляет больше памяти. Если есть такое требование, как поток парсера , он не должен завершаться , когда он получает OutOfMemoryError , вместо этого он должен обработать его и отправить сообщение / письмо администратору приложения / фреймворка.
Возникает ошибка, когда JVM больше не работает должным образом или находится на грани. Если вы поймаете ошибку, нет гарантии, что блок catch будет работать, и тем более, что он будет работать до конца.
Это также будет зависеть от работающего компьютера, текущего состояния памяти, поэтому нет возможности проверить, попытаться сделать все возможное. У вас будет только печальный результат.
Вы также понизите читабельность вашего кода.