Чи погана практика ловити 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 ()). Ваш блок вилучення виявить 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 - Використовуйте остаточний блок, щоб не потрібно забувати повторно:
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
що вимагає спеціального лікування. Детальнішу інформацію див. У розділі « Робота з InterruptException» .
Якщо ви хочете ловити лише неперевірені винятки, ви також можете врахувати цю схему
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
навколо всіх ваших запитувачів (сервлетів, операцій з підкосами або будь-якого контролера ....)
try{
//run the code which handles user request.
}catch(Throwable ex){
LOG.error("Exception was thrown: {}", ex);
//redirect request to a error page.
}
}
В якості іншого прикладу розглянемо, що у вас є клас обслуговування, який обслуговує бізнес з переказу коштів. Цей метод повертає a, 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
які інакше нешкідливі.Якщо ми використовуємо бросити , то він також охоплює помилку , і це все.
Приклад.
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 у пункті вилову, вона не лише буде вилучати всі винятки, але й буде вловлювати всі помилки. Помилки JVM видає, щоб вказати на серйозні проблеми, які не призначені для вирішення програми. Типовими прикладами для цього є OutOfMemoryError або StackOverflowError. І те й інше спричинене ситуаціями, які не підпадають під контроль програми та не можуть впоратися з ними. Таким чином, ви не повинні ловити Throables, якщо ви не впевнені, що це буде лише виняток, який проживає всередині 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
.