Захват броска и обработка особых исключений
Хорошо, я знаю, что ловить бросок - это не очень хорошая идея:
try {
// Some code
} catch(Throwable e) { // Not cool!
// handle the exception
}
Но недавно я читал открытый исходный код, и я увидел этот интересный (по крайней мере для меня) фрагмент кода:
try {
// Some Code
} catch (Throwable ex){
response = handleException(ex, resource);
}
private handleException(Throwable t, String resource) {
if (t instanceof SQLEXception) {
// Some code
} else if (t instanceof IllegalArgumentException) {
//some code
} //so on and so forth
}
Это не так уж плохо? Что не так с этим подходом?
Ответы
Ответ 1
Существуют различные причины, по которым вы не должны ловить Throwable. Прежде всего, это Throwable
включает Error
- и обычно не так много приложений, если появляется одно из них. Также Throwable снижает ваши шансы выяснить, ЧТО произошло. Все, что вы получаете, это "что-то плохое случилось" - это может быть катастрофа или просто неприятность.
Другой подход лучше, но, конечно, я все равно не поймаю Throwable, но попытаюсь поймать более конкретные Исключения, если возможно вообще. В противном случае вы все поймаете, а затем попытаетесь разобраться, что случилось. Ваш пример может быть написан как...
try {
...
} catch (SQLEXception ex){
response = ... ;
} catch (IllegalArgumentException ex){
response = ...;
}
..., что уменьшило бы количество блоков if ( ... instanceof ... )
(которые нужны только потому, что автор сначала решил поймать все в одном большом ковше). Это что-то на самом деле throws Throwable
, тогда у вас нет большого выбора, конечно.
Ответ 2
Вы правы, когда говорите, что ловить Throwable
не очень хорошая идея. Тем не менее, код, который вы представляете в своем вопросе, не ломает Throwable
злым образом, но расскажите об этом позже. На данный момент код, который вы представляете в своем вопросе, имеет несколько преимуществ:
1. Читаемость
Если вы внимательно посмотрите на код, вы заметите, что даже если блок catch поймал Throwable
, метод handleException
проверяет тип созданного исключения и, возможно, выполняет разные действия на основе типа исключения.
Код, представленный в вашем вопросе, является синонимом:
try {
doSomething();
} catch (SQLEXception ex){
response = handleException(resource);
} catch(IllegalArgumentException ex) {
response = handleException(resource);
} catch(Throwable ex) {
response = handleException(resource);
}
Даже если вам нужно поймать только 10+ исключений, этот код может легко взять много строк кода, а конструкция с несколькими ловушками не сделает код более чистым. Код, который вы представляете в своем вопросе, просто делегирует catch
другому методу, чтобы сделать фактический метод, который делает работу более читаемой.
2. Повторное использование
Код метода handleRequest может быть легко изменен и помещен в класс утилиты и доступен во всем приложении для обработки как Exception
и Error
s. Вы даже можете извлечь метод в два метода private
; Тот, который обрабатывает Exception
и тот, который обрабатывает Error
, и имеет метод handleException
, который принимает Throwable
, дополнительно делегирует вызовы этим методам.
3. Maintainibility
Если вы решите, что хотите изменить способ регистрации SQLException
в своем приложении, вы должны внести это изменение в одно место, а не посещать каждый метод в каждом классе, который генерирует SQLException
.
Так ловят Throwable
плохую идею?
Код, который вы представляете в своем вопросе, на самом деле не такой, как ловить Throwable
. Следующий фрагмент кода большой нет-нет:
try {
doSomething();
} catch(Throwable e) {
//log, rethrow or take some action
}
Вы должны поймать Throwable
или Exception
как можно дальше в цепочке catch
.
И последнее, но не менее важное, помните, что код, который вы представляете в своем вопросе, представляет собой код рамки, и есть определенные ошибки, от которых инфраструктура все еще может восстановиться. См. Когда вы можете поймать java.lang.Error для лучшего объяснения.
Ответ 3
Ловить Throwable
из лени - плохая идея.
Это было особенно заманчиво до того, как был введен try-multi-catch
.
try {
...
} catch (SomeException e) {
//do something
} catch (OtherException e) {
//do the same thing
} ...
Повторяющиеся блоки catch являются утомительными и многословными, поэтому некоторые люди решили просто поймать Exception
или Throwable
и сделать с ним. Этого следует избегать, потому что:
- Это затрудняет отслеживание того, что вы пытаетесь сделать.
- Вы можете в конечном итоге поймать много вещей, с которыми вы не можете справиться.
- Вы заслуживаете бонусного наказания, если вы полностью проглотите
Throwable
в блоке catch. (И мы все видели код, который делает это...:))
Но ловить Throwable
, когда это абсолютно необходимо, прекрасно.
Когда это необходимо? Очень редко. В коде в стиле рамки существуют различные сценарии (динамическая загрузка внешнего класса является наиболее очевидной), в отдельном приложении типичным примером является попытка отобразить/записать какое-то сообщение об ошибке перед выходом. (Принимая во внимание, что попытка может потерпеть неудачу, поэтому вы не хотите, чтобы там было что-то важное.)
Как правило, если вы ничего не можете сделать об исключении/ошибке, вы не должны его вообще поймать.
Ответ 4
Вы отправили ссылку на Jongo, которая демонстрирует одно возможное использование для этой техники: повторное использование кода обработки ошибок.
Скажем, у вас есть большой блок кода обработки ошибок, который, естественно, повторяется в разных местах вашего кода - например, Jongo производит стандартные ответы для некоторых стандартных классов ошибок. Может быть хорошей идеей извлечь этот код обработки ошибок в метод, чтобы вы могли повторно использовать его со всех мест, в которых он нуждался.
Однако, чтобы не сказать, что с кодом Jongo нет ничего плохого.
Захват Throwable
(а не использование multicatch) по-прежнему вызывает подозрение, так как вы, вероятно, поймаете Error
, что вы не можете обрабатывать (вы уверены, что хотите поймать ThreadDeath
?). В этой ситуации, если вам абсолютно нужно поймать Throwable
, было бы лучше "поймать и освободить" (т.е. Вернуть все, что вы не хотели поймать). Jongo этого не делает.
Ответ 5
Для использования огромной сети существует ровно два действительных использования:
-
Если вы будете обрабатывать все равномерно, например, захват верхнего уровня для ведения журнала/отчета, возможно, последующий немедленный выход.
-
Чтобы уменьшить дублирование, экспортируйте всю обработку в свой собственный метод.
Поймать самого производного общего предка, чтобы избежать лишней работы и повысить ясность.
DRY - важный принцип дизайна.
В обоих случаях, если вы не ожидали этого исключения и не обработали его полностью, выполните перезагрузку.
Ответ 6
Прежде всего, ловить Throwable делает ваше приложение довольно непрозрачным. Вы должны быть настолько ясными, насколько это возможно, для обнаружения исключений, чтобы обеспечить хорошую отслеживаемость в исключительных случаях.
Давайте рассмотрим метод handleException (...) и рассмотрим некоторые проблемы, возникающие при таком подходе:
- вы ловите Throwable, но вы используете только Исключения, что произойдет, если, например, OutOfMemoryError типа Ошибка выбрасывается? - Я вижу плохие вещи...
- Что касается хорошего объектно-ориентированного программирования с использованием instanceof, он прерывает открытый принцип и делает изменения кода (например, добавление новых исключений) действительно беспорядочным.
С моей точки зрения блоки catch-блоков точно созданы для функций, которые пытаются использовать в handleExceptions (...), поэтому используйте их.
Ответ 7
Java 7 решает немного скуки, которая представляет собой многопользовательский режим для подобных исключений с аналогичной обработкой. Вы определенно не должны делать то, что сделал здесь человек. Просто поймите соответствующие исключения по мере необходимости, он может выглядеть уродливым, но затем, для чего throws
, передать его методу, который должен его поймать, и вы не должны тратить слишком много места на код.
Проверьте эту ссылку для получения дополнительной информации.
Ответ 8
Просто для обеспечения баланса - есть one место, где всегда будет catch (Throwable)
:
public static void main(String args[]) {
try {
new Test().test();
} catch (Throwable t) {
t.printStackTrace(System.err);
}
}
По крайней мере, что-то показывает, что что-то пошло не так.
Ответ 9
Вы всегда можете улавливать разные типы исключений и выполнять некоторые операции на основе типа получаемого вами исключения.
Вот пример
try{
//do something that could throw an exception
}catch (ConnectException e) {
//do something related to connection
} catch (InvalidAttributeValueException e) {
// do anything related to invalid attribute exception
} catch (NullPointerException e) {
// do something if a null if obtained
}
catch (Exception e) {
// any other exception that is not handled can be catch here, handle it here
}
finally{
//perform the final operatin like closing the connections etc.
}