Это плохая практика - ловить Throwable
?
Например что-то вроде этого:
try {
// Some code
} catch(Throwable e) {
// handle the exception
}
Это плохая практика или мы должны быть как можно более конкретными?
Это плохая практика - ловить Throwable
?
Например что-то вроде этого:
try {
// Some code
} catch(Throwable e) {
// handle the exception
}
Это плохая практика или мы должны быть как можно более конкретными?
Ответы:
Вы должны быть максимально конкретными. Иначе таким образом могут исчезнуть непредвиденные ошибки.
Кроме того, Throwable
обложки Error
тоже, а это обычно не точка возврата . Вы не хотите ловить / обрабатывать это, вы хотите, чтобы ваша программа немедленно умерла, чтобы вы могли ее исправить.
Это плохая идея. На самом деле, даже поимка 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);
}
throw new Exception("Some additional info, eg. userId " + userId, e);
. Это будет зарегистрировано в одном приятном исключении с 10 причинами.
Также имейте в виду, что когда вы поймаете Throwable
, вы также можете поймать, InterruptedException
что требует особого обращения. Дополнительные сведения см. В разделе Работа с InterruptedException .
Если вы хотите поймать только непроверенные исключения, вы также можете рассмотреть этот шаблон
try {
...
} catch (RuntimeException exception) {
//do something
} catch (Error error) {
//do something
}
Таким образом, когда вы изменяете свой код и добавляете вызов метода, который может генерировать проверенное исключение, компилятор напомнит вам об этом, а затем вы можете решить, что делать в этом случае.
прямо из 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
Это неплохая практика, если у вас абсолютно не может быть пузыря исключений из метода.
Это плохая практика, если вы действительно не можете справиться с исключением. Лучше добавить к сигнатуре метода «выбросы», чем просто перехватить и повторно выбросить или, что еще хуже, обернуть его в RuntimeException и повторно выбросить.
Throwable
экземпляров - например, для пользовательского журнала исключений.
Поймать Throwable иногда необходимо, если вы используете библиотеки, которые с энтузиазмом бросают ошибки, иначе ваша библиотека может убить ваше приложение.
Однако при таких обстоятельствах лучше всего указать только конкретные ошибки, выдаваемые библиотекой, а не все объекты Throwables.
Throwable - это базовый класс для всех классов, которые могут быть выброшены (не только исключения). Вы мало что можете сделать, если поймаете OutOfMemoryError или KernelError (см. Когда поймать java.lang.Error? )
ловушки исключений должно быть достаточно.
это зависит от вашей логики или, если быть более конкретным, от ваших опций / возможностей. Если есть какое-либо конкретное исключение, на которое вы можете отреагировать осмысленным образом, вы можете сначала его поймать и сделать это.
Если его нет, и вы уверены, что сделаете то же самое для всех исключений и ошибок (например, выйдите с сообщением об ошибке), то поймать бросаемый объект не проблема.
Обычно держится первый футляр, и бросок не поймаешь. Но есть еще масса случаев, когда ловля работает нормально.
Хотя это описывается как очень плохая практика, иногда вы можете найти редкие случаи, когда это не только полезно, но и обязательно. Вот два примера.
В веб-приложении, где вы должны показать пользователю полную страницу с ошибкой. Этот код гарантирует, что это произойдет, поскольку он 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
throwable
, вот о чем этот вопрос
Вопрос немного расплывчатый; вы спрашиваете: «можно ли поймать Throwable
» или «можно ли поймать Throwable
и ничего не делать»? Многие здесь ответили на последнее, но это побочный вопрос; В 99% случаев вы не должны «потреблять» или отбрасывать исключение, независимо от того, ловите ли вы Throwable
или IOException
что-то еще.
Если вы распространяете исключение, ответ (как и ответ на очень многие вопросы) - «это зависит от обстоятельств». Это зависит от того, что вы делаете, за исключением того, почему вы его ловите.
Хороший пример того, почему вы хотели бы поймать, Throwable
- обеспечить некоторую очистку в случае какой-либо ошибки. Например, в JDBC, если во время транзакции возникает ошибка, вам нужно откатить транзакцию:
try {
…
} catch(final Throwable throwable) {
connection.rollback();
throw throwable;
}
Обратите внимание, что исключение не отбрасывается, а распространяется.
Но, как правило, ловить, Throwable
потому что у вас нет причины и вы слишком ленивы, чтобы увидеть, какие именно исключения выбрасываются, - это плохая форма и плохая идея.
Вообще говоря, вы хотите избежать перехвата Error
s, но я могу придумать (по крайней мере) два конкретных случая, когда это уместно:
AssertionError
безвредные в остальном.Если мы используем 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)
Throwable - это суперкласс всех ошибок и исключений. Если вы используете Throwable в предложении catch, он не только перехватит все исключения, но и все ошибки. JVM выдает ошибки, чтобы указать на серьезные проблемы, которые не предназначены для обработки приложением. Типичными примерами для этого являются OutOfMemoryError или StackOverflowError. Оба они вызваны ситуациями, которые находятся вне контроля приложения и не могут быть обработаны. Поэтому вам не следует ловить Throwables, если вы не уверены, что это будет только исключение, находящееся внутри Throwable.
Хотя, как правило, ловить 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
он перехватывается, а любые исключения просто регистрируются, позволяя продолжить работу остальной части потока.
Exception
любыми средствами, но не лови Throwable
.