Чи погана практика ловити 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 / Помилка для реєстрації проблеми - справа розумна ...
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 ()). Ваш блок вилучення виявить 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);
 }

4
+1 за те, що навіть ловити Виняток - це не добре. Принаймні, є ThreadInterruptedException, який вимагає особливої ​​обережності (коротше кажучи - після того, як його вловити, потрібно встановити статус перерваної нитки назад у значення 'true')
Кирило Гамазков

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

Як я можу дізнатися, чи є помилка / передача, якщо я її не знайду? Я нічого не бачив у журналах. Додаток Java EE. Щойно витрачені дні застрягли, не знаючи, в чому проблема, поки я не додав цей улов.
Філіп Рего

2
Я вважаю варіант №2 поганою практикою. Уявіть 10 ланцюгових дзвінків з журналом і повторним скиданням. Якщо ви подивитеся на файл журналу, ви не будете раді. Виняток становить 10 разів, що робить журнали дуже важкими для читання. ІМХО набагато краще робити throw new Exception("Some additional info, eg. userId " + userId, e);. Це буде записано в одному приємному винятку з 10 причин.
Петро Újezdský

21

Також пам’ятайте, що коли ви ловите Throwable, ви також можете ловити, InterruptedExceptionщо вимагає спеціального лікування. Детальнішу інформацію див. У розділі « Робота з InterruptException» .

Якщо ви хочете ловити лише неперевірені винятки, ви також можете врахувати цю схему

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
Дійсно, якщо у вас є вибір ;-)
ДНК

ось найбільша проблема з ловом метальних предметів та їх повторним закиданням. це дійсно робить неможливим інтерфейс для всіх методів вище за потоком у стеці. Їм або доводиться мати справу з підкинутим підписом, або мають непридатний підпис, який можна підкинути, з яким повинні мати справу інші.
Ендрю Норман

6

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

Вилов Винятків має бути достатньо.


5

це залежить від вашої логіки або бути більш конкретним для ваших варіантів / можливостей. Якщо є якийсь конкретний виняток, на який, можливо, ви зможете реагувати змістовно, ви можете спочатку його зловити і зробити це.

Якщо цього немає, і ви впевнені, що зробите те саме для всіх винятків і помилок (наприклад, вихід із повідомленням про помилку), ніж це не проблема, щоб зловити викинуті.

Зазвичай перший випадок має місце, і ви не зможете зловити кинуті. Але все ж є маса випадків, коли ловити його добре.


4

Хоча це описується як дуже погана практика, іноді можна зустріти рідкісні випадки, що це не тільки корисно, але й обов’язково. Ось два приклади.

У веб-програмі, де ви повинні показати користувачеві значення загальної сторінки помилок. Цей код переконайтеся, що це відбувається, оскільки він є великим 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


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

Якщо ми використовуємо бросити , то він також охоплює помилку , і це все.

Приклад.

    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 у пункті вилову, вона не лише буде вилучати всі винятки, але й буде вловлювати всі помилки. Помилки JVM видає, щоб вказати на серйозні проблеми, які не призначені для вирішення програми. Типовими прикладами для цього є OutOfMemoryError або StackOverflowError. І те й інше спричинене ситуаціями, які не підпадають під контроль програми та не можуть впоратися з ними. Таким чином, ви не повинні ловити Throables, якщо ви не впевнені, що це буде лише виняток, який проживає всередині Throwable.


-1

Хоча загальноприйнятною практикою є ловлі метальних (як це з’ясовується численними відповідями на це питання), сценарії, коли лов 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.
andrewf
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.