2015-07-23 3 views
21

Хорошо я знаю ловлю Throwable это не очень хорошая идея:Ловля Throwable и обработки специфических исключений

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 
    } 

Это не похоже на то, что плохо? Что не так с этим подходом?

+4

Иногда, особенно в рамках, у вас нет другого выбора, кроме как поймать 'Throwable'. Это не очень хорошая идея в коде уровня приложения, если вы не хотите иметь какой-то «обработчик последней инстанции». – biziclop

+0

У вас есть ссылка на открытый исходный код (например, исходный файл на GitHub)? Я могу представить несколько вариантов использования, где, в зависимости от контекста, это может быть хорошей стратегией. –

+0

@James_pic там вы идете https://github.com/Eldelshell/jongo/blob/master/jongo-core/src/main/java/jongo/RestController.java –

ответ

17

Существуют различные причины, по которым вы не должны ловить Throwable. Прежде всего, что Throwable включает в себя Error s - и, как правило, мало что может сделать приложение, если появится одно из них. Также Throwable снижает ваши шансы выяснить, ЧТО произошло. Все, что вы получаете, это «что-то плохое случилось» - это может быть катастрофа или просто неприятность.

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

try { 
    ... 
} catch (SQLEXception ex){ 
    response = ... ; 
} catch (IllegalArgumentException ex){ 
    response = ...; 
} 

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

+2

Проблема в том, что есть 'Error's' которые легко восстановить. Если фреймворк пытается загрузить класс, и он терпит неудачу, все может быть в порядке. – biziclop

+0

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

+3

Нет, я имел в виду, что сама инфраструктура должна поймать ошибку и разобраться с ней. К сожалению, авторы Java не различали восстановимые и невосстанавливаемые ошибки, поэтому иногда (очень редко) ловить «Throwable» - единственный разумный вариант. – biziclop

17

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

1. читаемость

Если посмотреть внимательно на код, вы увидите, что даже если блок поймать догоняет 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 с. Вы даже можете извлечь метод на два метода 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 цепи, как это возможно.

Последнее, но не менее важно, помните, что код, который вы представляете в своем вопросе, является кодом фреймворка, и есть определенные ошибки, от которых инфраструктура все еще может восстановиться. См. When to catch java.lang.Error для лучшего объяснения.

+0

Почему вы используете «Throwable» для этого, а не только «Исключение»? Этот метод нужно будет назвать handleThrowable ... – maraca

+2

@maraca Помните, что это код рамки. Для работы в Fameworks необходимо уловить ошибки, которые можно восстановить. (Первым примером являются ошибки связи). При этом я согласен с тем, что имя метода, которое они использовали, на самом деле не является хорошим выбором, но это не связано с фактическим вопросом. – CKing

+0

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

9

Поймать Throwable s из лени - плохая идея.

Это было особенно заманчиво до введения try-multi-catch.

try { 
    ... 
} catch (SomeException e) { 
    //do something 
} catch (OtherException e) { 
    //do the same thing 
} ... 

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

  1. Это затрудняет отслеживание того, что вы пытаетесь сделать.
  2. Вы можете в конечном итоге поймать много вещей, с которыми вы не можете справиться.
  3. Вы заслуживаете наказания в виде бонуса, если вы полностью проглотите Throwable в блоке catch. (И мы все видели код, который делает это ... :))

Но ловить Throwable сек, когда это абсолютно необходимо в порядке.

Когда это необходимо? Очень редко. В коде в стиле рамки существуют различные сценарии (динамическая загрузка внешнего класса является наиболее очевидной), в отдельном приложении типичным примером является попытка отобразить/записать какое-то сообщение об ошибке перед выходом. (Принимая во внимание, что попытка может потерпеть неудачу, поэтому вы не хотите размещать там что-либо критическое.)

Как правило, если вы ничего не можете сделать об исключении/ошибке, вы не должны поймать его вообще.

2

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

Давайте посмотрим на метод handleException (...) и увидеть некоторые из проблем, которые возникают при таком подходе:

  • вы поймать Throwable но обрабатывать только исключения, что происходит, если, например,OutOfMemoryError типа Ошибка выбрасывается? - Я вижу плохие вещи ...
  • Что касается хорошего объектно-ориентированного программирования с использованием instanceof, он прерывает открытый принцип и делает изменения кода (например, добавление новых исключений) действительно беспорядочным.

С моей точки зрения блоки блокировки точно выполнены для функций, которые пытаются покрыть handleEceptions (...), поэтому используйте их.

2

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

Check out this link for more information.

2

Просто, чтобы обеспечить баланс - есть один место, где я всегда буду catch (Throwable):

public static void main(String args[]) { 
    try { 
     new Test().test(); 
    } catch (Throwable t) { 
     t.printStackTrace(System.err); 
    } 
} 

По крайней мере, что-то где-то показывает, что что-то пошло не так.

+0

Действительно ??? Я никогда не делаю ... так как он всегда печатается. Может быть, это ОС? – maaartinus

3

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

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

Однако, это не значит, что в коде Чонго нет ничего плохого.

Ловля Throwable (а не с помощью multicatch) по-прежнему с подозрением, так как вы, вероятно, поймать Error S, которые вы на самом деле не в состоянии справиться (вы уверены вы имели в виду, чтобы поймать ThreadDeath?). В этой ситуации, если вам абсолютно нужно поймать Throwable, было бы лучше «поймать и освободить» (т. Е. Вернуть все, что вы не хотели поймать). Чонго этого не делает.

3

Есть ровно два действительные использует для использования огромной сети:

  • Если вы будете обрабатывать все равномерно, как улов верхнего уровня для регистрации/отчетности, возможно, с последующим немедленным выходом.

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

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

0

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

Вот пример

  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. 
      } 
Смежные вопросы