Перезавантаження винятків на Java, не втрачаючи слід стека


417

У C # я можу використовувати throw;оператор, щоб повторно скинути виняток, зберігаючи слід стека:

try
{
   ...
}
catch (Exception e)
{
   if (e is FooException)
     throw;
}

Чи є щось подібне в Java ( що не втрачає оригінальний слід стека )?


4
Чому, на вашу думку, він втрачає оригінальний стек? Єдиний спосіб його втратити, коли ви кидаєте нове SomeOtherException і забудете призначити першопричину в конструкторі або в initCause ().
akarnokd

4
Я вважаю, що таким чином поводиться код. Net, але я більше не позитивний. Можливо, варто або десь заглянути або провести невеликий тест.
ripper234

11
Throwables не змінюються, кидаючи їх. Щоб оновити слід стека, вам потрібно зателефонувати fillInStackTrace(). Зручно цей метод викликається в конструкторі a Throwable.
Роберт

50
У C # так, throw e;втратить стек-тракт. Але не на Java.
Тім Гудман

Відповіді:


560
catch (WhateverException e) {
    throw e;
}

просто повторно скине виняток, який ви потрапили (очевидно, що навколишній метод повинен дозволити це через свій підпис тощо). Виняток буде підтримувати початковий слід стека.


4
Привіт, InterruptException e видає некероване повідомлення про виняток, коли я додаю рядок кидання e. Не так, якщо я заміню його ширшим винятком e. Як це робити правильно?
Джеймс П.

1
@James, я щойно помітив, що повідомлення проходить, якщо в декларації функції додається "кидає XxxException".
shiouming

2
У Java 7 компілятор для такого повторного відкидання є більш розумним. Тепер він відмінно працює з винятками "кидки" у методі, що містить.
Вальдемар Восінський

193
@James Якщо ви catch(Exception e) { throw e; }це будете не обробляти. Якщо ви catch(InterruptedException ie) { throw ie; }це будете обробляти. Як правило, не варто catch(Exception e)- це не покемон, і ми не хочемо їх усіх спіймати!
corsiKa

3
@corsiKa Це не обов'язково правда, що ви не хочете "Ловити їх усіх", це просто інший випадок використання. Якщо у вас є цикл вищого рівня або обробник подій (наприклад, всередині запуску потоку), якщо ви не зловите принаймні RuntimeException і записуєте його, ви часто пропустите виняток і мовчки вирветеся з важливого циклу для чого часто є одноразовим збоєм. Це також дуже добре для функціональності плагінів, де ви не знаєте, що може зробити або кинути додатковий код ... Для таких способів зверху вниз, як ці вилучення, виняток часто є не лише хорошою ідеєю, а найкращою практикою.
Білл К

82

Я волів би:

try
{
    ...
}
catch (FooException fe){
   throw fe;
}
catch (Exception e)
{
    // Note: don't catch all exceptions like this unless you know what you
    // are doing.
    ...
}

6
Безумовно, належним чином у Java можна вибирати конкретні винятки, ніж загальні та перевіряти, наприклад,. +1
amischiefr

8
-1 тому що ви ніколи не повинні ловити звичайний "Виняток", якщо ви не знаєте, що робите.
Стробоскоп

19
@Stroboskop: правда, але для відповіді найкраще використовувати той же (аналогічний) код, що і в питанні!
користувач85421

14
Іноді ловити всі винятки - це нормально. Наприклад, коли ви пишете тестовий випадок. Або для лісозаготівельних робіт. Або в основному, де не ловити - означає врізатися.
Джон Генкель

1
@JohnHenckel та інші: дійсні бали занурені. Я оновив питання, щоб зрозуміти, що ловлі, Exceptionяк правило, не є правильною справою в більшості (але не у всіх) випадках.
Пер Лундберг

74

Ви також можете обернути виняток в інший І зберегти початковий слід стека, передавши в пункт «Виняток» як «Throwable» як параметр причини:

try
{
   ...
}
catch (Exception e)
{
     throw new YourOwnException(e);
}

8
Я також радив би додати повідомлення поряд із цим, використовуючиthrow new YourOwnException("Error while trying to ....", e);
Жульєн

це те, що я шукав, особливо версія з першого коментаря, де ви можете передати своє власне повідомлення
Csaba

Це правильно показує повідомлення про помилку, але слід стека показує рядок помилки як рядок із "киньте нове ....... (e)", а не оригінальним рядком, який спричинив виняток.
Ешберн РК

22

У Java майже те саме:

try
{
   ...
}
catch (Exception e)
{
   if (e instanceof FooException)
     throw e;
}

5
Ні, до тих пір, поки ви не будете призначати новий об'єкт Exception, стек трек залишається таким же.
Мнемент

28
Я б додав конкретний улов для FooException
dfa

3
У цьому конкретному випадку я погоджуюся, але додавання конкретного улову може бути не правильним вибором - уявіть, у вас є якийсь загальний код для всіх винятків і після, за певним винятком, повторно скинути його.
alves

1
@MarkusLausberg Але, нарешті, не виходить винятків.
Роберт

Так, але це не було питання.
Маркус Лоусберг

14

У Java ви просто кидаєте виняток, який ви зловили, throw eа не просто throw. Java підтримує слід стека.


6

щось на зразок цього

try 
{
  ...
}
catch (FooException e) 
{
  throw e;
}
catch (Exception e)
{
  ...
}

5
public int read(byte[] a) throws IOException {
    try {
        return in.read(a);
    } catch (final Throwable t) {
        /* can do something here, like  in=null;  */
        throw t;
    }
}

Це конкретний приклад, коли метод кидає an IOException. У finalзасобі tможе містити тільки виключення кинутого з блоку Try. Додатковий матеріал для читання можна знайти тут і тут .



3

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

try{ ... }catch (FooException e){ throw new BarException("Some usefull info", e); }


2

У мене просто була подібна ситуація, коли мій код потенційно викидає низку різних винятків, які я просто хотів перекинути. Вищеописане рішення не працювало для мене, тому що Eclipse сказав мені, що це throw e;призводить до нерозбірливого винятку, тому я просто зробив це:

try
{
...
} catch (NoSuchMethodException | SecurityException | IllegalAccessException e) {                    
    throw new RuntimeException(e.getClass().getName() + ": " + e.getMessage() + "\n" + e.getStackTrace().toString());
}

Для мене працювали .... :)

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.