Исключение, брошенное в блок захвата - будет ли оно снова поймано? - PullRequest
158 голосов
/ 27 сентября 2008

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

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

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

Ответы [ 9 ]

192 голосов
/ 27 сентября 2008

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

62 голосов
/ 27 сентября 2008

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

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 (кроме глупых случаев, таких как бесконечные циклы, присоединение через интерфейс инструментов и уничтожение потока, перезапись байт-кода и т. Д.).

26 голосов
/ 28 сентября 2008

Спецификация языка 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

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

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

6 голосов
/ 14 июня 2013

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

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

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

4 голосов
/ 27 сентября 2008

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

1 голос
/ 28 сентября 2008

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

1 голос
/ 27 сентября 2008

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

try {
    doSomething();
} catch (IOException) {
   try {
       doSomething();
   } catch (IOException e) {
       throw new ApplicationException("Failed twice at doSomething" +
       e.toString());
   }          
} catch (Exception e) {
}
1 голос
/ 27 сентября 2008

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

0 голосов
/ 01 декабря 2013

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

try {
  // Do something
} catch(IOException ioE) {
  throw new ApplicationException("Problem connecting to server");
} catch(Exception e) {
  // Will the ApplicationException be caught here?
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...