Исключение брошено внутри блока catch - будет ли оно поймано снова?

Это может показаться вопросом программирования 101, и я подумал, что знаю ответ, но теперь мне нужно дважды проверить. В этом фрагменте кода, будет ли исключение, заброшенное в первом блоке catch, быть уловлено общим блоком catch Exception ниже?

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

Я всегда думал, что ответ будет отрицательным, но теперь у меня есть странное поведение, которое может быть вызвано этим. Ответ, вероятно, одинаковый для большинства языков, но я работаю на Java.

Ответ 1

Нет, поскольку новый throw не находится в блоке try.

Ответ 2

Нет. Это очень легко проверить.

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");
        }
    }
}

Должен печатать:

In catch IOException: class java.io.IOException
In finally
Exception in thread "main" java.lang.RuntimeException
        at Catch.main(Catch.java:8)

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

Также обратите внимание: если вы обмениваетесь двумя блоками catch, он не будет компилироваться. Второй улов будет полностью недостижим.

Обратите внимание, что блок finally всегда выполняется, даже если выполняется блок catch (кроме глупых случаев, таких как бесконечные петли, прикрепление через интерфейс инструментов и уничтожение потока, переписывание байт-кода и т.д.).

Ответ 3

Спецификация языка Java говорит в разделе 14.19.1:

Если выполнение блока try завершается внезапно из-за броска значения V, тогда есть выбор:

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

Ссылка: http://java.sun.com/docs/books/jls/second_edition/html/statements.doc.html#24134

Другими словами, первый охватывающий catch, который может обрабатывать исключение, делает, и если исключение выбрасывается из этого улова, это не входит в сферу любого другого улова для первоначальной попытки, поэтому они не будут пытаться обрабатывать Это.

Одна связанная и запутанная вещь, которую нужно знать, заключается в том, что в структуре try- [catch] -finally блок finally может генерировать исключение, и если это так, любое исключение, созданное блоком try или catch, теряется. Это может сбивать с толку в первый раз, когда вы его видите.

Ответ 4

Если вы хотите выбросить исключение из блока catch, вы должны сообщить свой метод/класс/etc. что ему нужно выбросить указанное исключение. Например:

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

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

Ответ 5

Нет - как сказал Крис Джет-Янг, он будет переброшен в следующую попытку в иерархии.

Ответ 6

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

Ответ 7

Он не будет пойман вторым блоком catch. Каждое исключение захватывается только внутри блока try. Вы можете вложенные попытки (хотя это не хорошая идея вообще):

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

Ответ 8

Нет, поскольку уловы все относятся к одному и тому же блоку try, поэтому выброс из блока catch будет пойман с помощью прилагаемого блока try (вероятно, в методе, который вызвал этот)

Ответ 9

Старый пост, но переменная "e" должна быть уникальной:

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