Это плохая практика - ловить Throwable?


106

Это плохая практика - ловить Throwable?

Например что-то вроде этого:

try {
    // Some code
} catch(Throwable e) {
    // handle the exception
}

Это плохая практика или мы должны быть как можно более конкретными?


Ответы:


104

Вы должны быть максимально конкретными. Иначе таким образом могут исчезнуть непредвиденные ошибки.

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


38
Бывают ситуации, когда уместно перехватить ошибку и продолжить. Пример: в сервлете, если вы включаете OutOfMemoryError из-за того, что конкретный запрос съедает всю память, вы можете попытаться продолжить, поскольку объекты будут GC после обработки запроса. То же самое и с ошибкой утверждения. Вы не закрываете приложение, потому что что-то пошло не так с запросом.
gawi

7
Как узнать, что было выделено, а что не было до OOME? Как только вы это получите, все ставки будут отменены, даже внутри контейнера J2EE, такого как Tomcat или JBoss.
bmauter

10
У нас был NoSuchMethodError, и, к счастью, мы не уничтожили всех наших клиентов, отключив сервер, как это произошло через две недели после развертывания. т.е. У нас всегда есть возможность поймать Throwable и сделать все возможное, чтобы обработать и отправить ошибку клиенту. В конце концов, существует множество типов ошибок, которые можно исправить, поскольку они могут затронуть только 1 из 1000 клиентов.
Дин Хиллер,

10
" вы хотите, чтобы ваша программа немедленно умерла, чтобы вы могли ее исправить " => если ваша программа умирает, как узнать, что произошло? Уловить Throwable / Error для регистрации проблемы - разумный поступок ...
assylias

3
@assylias Автономное приложение вызовет фатальную ошибку для stderr.
Филип Уайтхаус

36

Это плохая идея. На самом деле, даже поимка Exception- плохая идея. Рассмотрим пример:

try {
    inputNumber = NumberFormat.getInstance().formatNumber( getUserInput() );
} catch(Throwable e) {
    inputNumber = 10; //Default, user did not enter valid number
}

Теперь предположим, что getUserInput () на время блокируется, а другой поток останавливает ваш поток худшим из возможных способов (он вызывает thread.stop ()). Ваш блок catch перехватит ThreadDeathошибку. Это очень плохо. Поведение вашего кода после обнаружения этого исключения в значительной степени не определено.

Аналогичная проблема возникает при перехвате исключения. Возможно, getUserInput()произошел сбой из-за InterruptException, исключения из-за отказа в разрешении при попытке записать результаты или всевозможных других сбоев. Вы понятия не имеете, что пошло не так, поскольку из-за этого вы также не знаете, как решить проблему.

У вас есть три лучших варианта:

1 - Поймать именно те исключения, которые вы умеете обрабатывать:

try {
    inputNumber = NumberFormat.getInstance().formatNumber( getUserInput() );
} catch(ParseException e) {
    inputNumber = 10; //Default, user did not enter valid number
}

2 - Повторите вызов любого исключения, с которым вы столкнулись и не знаете, как с ним справиться:

try {
    doSomethingMysterious();
} catch(Exception e) {
    log.error("Oh man, something bad and mysterious happened",e);
    throw e;
}

3 - Используйте блок finally, чтобы не забывать о повторном броске:

 Resources r = null;
 try {
      r = allocateSomeResources();
      doSomething(r);
 } finally {
     if(r!=null) cleanUpResources(r);
 }

4
+1 за то, что даже ловить Exception не годится. По крайней мере, есть исключение ThreadInterruptedException, которое требует особой осторожности (короче - после его обнаружения вам нужно вернуть статус прерывания потока обратно в значение «true»)
Кирилл Гамазков

Я знаю, что это просто для иллюстрации ваших слов, но я думаю, вы можете проверить с помощью регулярного выражения, является ли ваш пользовательский ввод буквенно-цифровым или какой формат вам нужен, и не используйте каждый раз try catch везде.
amdev

Как я могу узнать, есть ли ошибка / бросок, если я ее не улавливаю? В журналах ничего не увидел. Приложение Java EE. Просто провел дни, не зная, в чем проблема, пока я не добавил этот улов.
Филип Рего

2
Вариант №2 считаю плохой практикой. Представьте себе 10 связанных вызовов с логированием и повторным вызовом. Если вы посмотрите файл журнала, вы не останетесь довольны. Исключение регистрируется 10 раз, что затрудняет чтение журналов. ИМХО намного лучше это сделать throw new Exception("Some additional info, eg. userId " + userId, e);. Это будет зарегистрировано в одном приятном исключении с 10 причинами.
Petr Újezdský

21

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

Если вы хотите поймать только непроверенные исключения, вы также можете рассмотреть этот шаблон

try {
   ...
} catch (RuntimeException exception) {
  //do something
} catch (Error error) {
  //do something
}

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


14

прямо из javadoc класса Error (который рекомендует не ловить их):

 * An <code>Error</code> is a subclass of <code>Throwable</code> 
 * that indicates serious problems that a reasonable application 
 * should not try to catch. Most such errors are abnormal conditions. 
 * The <code>ThreadDeath</code> error, though a "normal" condition,
 * is also a subclass of <code>Error</code> because most applications
 * should not try to catch it. 

 * A method is not required to declare in its <code>throws</code> 
 * clause any subclasses of <code>Error</code> that might be thrown 
 * during the execution of the method but not caught, since these 
 * errors are abnormal conditions that should never occur. 
 *
 * @author  Frank Yellin
 * @version %I%, %G%
 * @see     java.lang.ThreadDeath
 * @since   JDK1.0

13

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

Это плохая практика, если вы действительно не можете справиться с исключением. Лучше добавить к сигнатуре метода «выбросы», чем просто перехватить и повторно выбросить или, что еще хуже, обернуть его в RuntimeException и повторно выбросить.


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

9

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

Однако при таких обстоятельствах лучше всего указать только конкретные ошибки, выдаваемые библиотекой, а не все объекты Throwables.


12
Или использовать лучше написанную библиотеку?
Raedwald

6
Действительно, если у вас есть выбор ;-)
DNA

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

6

Throwable - это базовый класс для всех классов, которые могут быть выброшены (не только исключения). Вы мало что можете сделать, если поймаете OutOfMemoryError или KernelError (см. Когда поймать java.lang.Error? )

ловушки исключений должно быть достаточно.


5

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

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

Обычно держится первый футляр, и бросок не поймаешь. Но есть еще масса случаев, когда ловля работает нормально.


4

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

В веб-приложении, где вы должны показать пользователю полную страницу с ошибкой. Этот код гарантирует, что это произойдет, поскольку он try/catchважен для всех ваших обработчиков запросов (сервлеты, действия struts или любой контроллер ...)

try{
     //run the code which handles user request.
   }catch(Throwable ex){
   LOG.error("Exception was thrown: {}", ex);
     //redirect request to a error page. 
 }

}

В качестве другого примера представьте, что у вас есть класс обслуживания, который обслуживает бизнес по переводу средств. Этот метод возвращает, TransferReceiptесли передача произведена или NULLнет.

String FoundtransferService.doTransfer( fundtransferVO);

Теперь вы получаете информацию Listо денежных переводах от пользователя, и вы должны использовать вышеуказанный сервис, чтобы сделать их все.

for(FundTransferVO fundTransferVO : fundTransferVOList){
   FoundtransferService.doTransfer( foundtransferVO);
}

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

for(FundTransferVO fundTransferVO : fundTransferVOList){
    FoundtransferService.doTransfer( foundtransferVO);
 }catch(Throwable ex){
    LOG.error("The transfer for {} failed due the error {}", foundtransferVO, ex);
  }
}

Вы можете просмотреть множество проектов с открытым исходным кодом, чтобы убедиться, что они throwableдействительно кэшируются и обрабатываются. Например здесь является поиск tomcat, struts2и primefaces:

https://github.com/apache/tomcat/search?utf8=%E2%9C%93&q=catch%28Throwable https://github.com/apache/struts/search?utf8=%E2%9C%93&q=catch % 28Throwable https://github.com/primefaces/primefaces/search?utf8=%E2%9C%93&q=catch%28Throwable


1
Видел код в этих ссылках. Метательный - это не только тот, что ловится! Перед Throwable есть и другие исключения.
developer1011

@ developer101, конечно, но они улавливают throwable, вот о чем этот вопрос
Алиреза Фаттахи

4

Вопрос немного расплывчатый; вы спрашиваете: «можно ли поймать Throwable» или «можно ли поймать Throwableи ничего не делать»? Многие здесь ответили на последнее, но это побочный вопрос; В 99% случаев вы не должны «потреблять» или отбрасывать исключение, независимо от того, ловите ли вы Throwableили IOExceptionчто-то еще.

Если вы распространяете исключение, ответ (как и ответ на очень многие вопросы) - «это зависит от обстоятельств». Это зависит от того, что вы делаете, за исключением того, почему вы его ловите.

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

try {
  
} catch(final Throwable throwable) {
  connection.rollback();
  throw throwable;
}

Обратите внимание, что исключение не отбрасывается, а распространяется.

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


1

Вообще говоря, вы хотите избежать перехвата Errors, но я могу придумать (по крайней мере) два конкретных случая, когда это уместно:

  • Вы хотите закрыть приложение в ответ на ошибки, особенно AssertionError безвредные в остальном.
  • Вы реализуете механизм объединения потоков, аналогичный ExecutorService.submit (), который требует, чтобы вы пересылали исключения обратно пользователю, чтобы он мог их обработать.

0

Если мы используем throwable , он также охватывает Error и все.

Пример.

    public class ExceptionTest {
/**
 * @param args
 */
public static void m1() {
    int i = 10;
    int j = 0;
    try {
        int k = i / j;
        System.out.println(k);
    } catch (Throwable th) {
        th.printStackTrace();
    }
}

public static void main(String[] args) {
    m1();
}

}

Вывод:

java.lang.ArithmeticException: / by zero
at com.infy.test.ExceptionTest.m1(ExceptionTest.java:12)
at com.infy.test.ExceptionTest.main(ExceptionTest.java:25)

0

Throwable - это суперкласс всех ошибок и исключений. Если вы используете Throwable в предложении catch, он не только перехватит все исключения, но и все ошибки. JVM выдает ошибки, чтобы указать на серьезные проблемы, которые не предназначены для обработки приложением. Типичными примерами для этого являются OutOfMemoryError или StackOverflowError. Оба они вызваны ситуациями, которые находятся вне контроля приложения и не могут быть обработаны. Поэтому вам не следует ловить Throwables, если вы не уверены, что это будет только исключение, находящееся внутри Throwable.


-1

Хотя, как правило, ловить Throwable - это плохая практика (о чем свидетельствуют многочисленные ответы на этот вопрос), сценарии, в которых отлов Throwableполезен, довольно распространены. Позвольте мне объяснить один такой случай, который я использую на своей работе, на упрощенном примере.

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

public Integer addNumbers(Integer a, Integer b) {
    Integer c = a + b;          //This will throw a NullPointerException if either 
                                //a or b are set to a null value by the
                                //calling method
    successfulAdditionAlert(c);
    return c;
}

private void successfulAdditionAlert(Integer c) {
    try {
        //Code here to read configurations and send email alerts.
    } catch (Throwable e) {
        //Code to log any exception that occurs during email dispatch
    }
}

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


Я бы попытался избежать этого, создав поток, посвященный задаче (с очередью) отправки электронных писем.
boumbh

Я бы предположил, что это все еще плохой код. Лови Exceptionлюбыми средствами, но не лови Throwable.
Эндрюф
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.