Виняток, кинутий всередині блоку лову - чи буде його знову спіймано?


180

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

try {
  // Do something
} catch(IOException e) {
  throw new ApplicationException("Problem connecting to server");
} catch(Exception e) {
  // Will the ApplicationException be caught here?
}

Я завжди думав, що відповідь буде «ні», але зараз у мене є якась дивна поведінка, яка може бути викликана цим. Відповідь, мабуть, однакова для більшості мов, але я працюю на Java.


2
Можливо, ви могли б описати "дивну поведінку"?
Джефрі Л Уітлідж

Ви впевнені, що ApplicationException не перекидається в інше місце і не розповсюджується до цього блоку?
sblundy

Я помітив це в моєму IDE Eclipse. Мене змушує мене поставити "кинути новий виняток" у блок проб, але я не знаю чому. Я робив це в минулому, не роблячи цього. Я не розумію, чому потрібно буде пробний блок. Багато прикладів у Google показують, що люди не потребують спробувати. Це тому, що я кидаю всередину оператор if?
djangofan

Відповіді:


214

Ні, оскільки нове throwне знаходиться в tryблоці безпосередньо.


Скажімо, якщо код такий, спробуйте {} catch (Виняток e) {System.err.println ("In Exception:" + e.getClass ()); } catch (IOException e) {System.err.println ("In catch IOException:" + e.getClass ()); } і код у пробному блоці генерує IO Exception, чи перейде він до безпосереднього загального блоку винятків, або він перелетить до блоку спіймання IOException?
sofs1

4
@ user3705478 Код не збирається, щоб уникнути подібної помилкової ситуації. Загалом, вам не дозволяється мати улов для підкласу після виходу його суперкласу в той же блок спробу.
Кріс Єстер-Янг

Дякую. Тож я отримаю помилку часу компіляції, правда? Я випробую це, коли повернусь додому.
sofs1

Це залежить від @ user3705478, якщо a RuntimeExceptionвикидається з catchблоку, помилки компіляції не буде.
Ренді Дев

@AndrewDunn Я не думаю, що це питання user3705478, а, швидше, що трапиться, якщо catchстаттю про виключення батьків перераховано перед дочірнім винятком catch. Я розумію, що Java це забороняє, і це потрапляє під час компіляції.
Кріс Єстер-Янг

71

Ні. Це дуже легко перевірити.

public class Catch {
    public static void main(String[] args) {
        try {
            throw new java.io.IOException();
        } catch (java.io.IOException exc) {
            System.err.println("In catch IOException: "+exc.getClass());
            throw new RuntimeException();
        } catch (Exception exc) {
            System.err.println("In catch Exception: "+exc.getClass());
        } finally {
            System.err.println("In finally");
        }
    }
}

Слід надрукувати:

У лові IOException: клас java.io.IOException
Нарешті
Виняток у потоці "main" java.lang.RuntimeException
        на Catch.main (Catch.java:8)

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

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

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


3
Найбільш очевидний спосіб уникнути - finallyце, звичайно, дзвонити System.exit. :-P
Кріс Єстер-Янг

3
@Chris Jester-Young for(;;);коротше, міститься в мові, не вводить багато в спосіб побічних ефектів і, для мене, більш очевидний.
Том Хотін - тайклін

1
System.exitдружніше до процесора! : -О Але так, добре, явно це суб'єктивний критерій. Крім того, я не знав, що ти гравець у коді. ;-)
Кріс Єстер-Янг

28

У розділі 14.19.1 вказано специфікацію мови Java:

Якщо виконання блоку спробу завершено різко через кидок значення V, то є вибір:

  • Якщо тип V під час виконання V присвоюється Параметру будь-якого застереження про запуск оператора спробу, тоді вибирається перший (крайній лівий) такий пункт зачеплення. Значення V присвоюється параметру вибраного пункту лову, і виконується блок цього пункту лову. Якщо цей блок завершується нормально, то оператор try завершується нормально; якщо цей блок різко завершується з будь-якої причини, то оператор спробу завершується різко з тієї ж причини.

Довідка: http://java.sun.com/docs/books/jls/second_edition/html/statements.doc.html#24134

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

Одне пов'язане та заплутане, що потрібно знати, це те, що в структурі try- [catch], нарешті, блок нарешті може кинути виняток, і якщо так, будь-який виняток, кинутий блоком try або catch, втрачається. Це може бентежити, коли ви це побачите.


Просто хотів додати, що з Java 7 ви можете уникати цього, використовуючи try-with-ресурси. Тоді, якщо tryІ finallyобидва кидають, finallyпридушується, але також додається до виключення з try. Якщо і catchкидки, вам не пощастить, якщо ви самі не впораєтеся з цим через "addSuppression" і додасте tryвиняток - тоді у вас є все три.
LAFK повідомляє, що відбудеться повернення Моніки

6

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

public void doStuff() throws MyException {
    try {
        //Stuff
    } catch(StuffException e) {
        throw new MyException();
    }
}

І тепер ваш компілятор не буде на вас кричати :)


4

Ні - Як сказав Кріс Єстер-Янг, це буде перенесено на наступний досвід в ієрархії.


2

Як було сказано вище ...
Я додам, що якщо у вас виникли проблеми з переглядом того, що відбувається, якщо ви не можете відтворити проблему в налагоджувачі, ви можете додати слід перед повторним перекиданням нового винятку (за допомогою старої гарної системи .out.println в гіршому випадку, з хорошою системою журналів, як log4j в іншому випадку).


2

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

try {
    doSomething();
} catch (IOException) {
   try {
       doSomething();
   } catch (IOException e) {
       throw new ApplicationException("Failed twice at doSomething" +
       e.toString());
   }          
} catch (Exception e) {
}

Я написав подібний код. Але я не переконаний. Я хочу зателефонувати Thread.sleep () у свій блок запису. Але Thread.sleep кидає собі InterruptedException. Чи правильно (найкраща практика) робити це так, як ви показали у своєму прикладі?
riroo

1

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


-4

Стара змінна публікація, але "e" повинна бути унікальною:

try {
  // Do something
} catch(IOException ioE) {
  throw new ApplicationException("Problem connecting to server");
} catch(Exception e) {
  // Will the ApplicationException be caught here?
}

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