Подтвердить что ты не робот

Захват броска и обработка особых исключений

Хорошо, я знаю, что ловить бросок - это не очень хорошая идея:

    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
    }

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

4b9b3361

Ответ 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.
             }