Исключение Java не поймано?
У меня есть небольшая теоретическая проблема с конструкциями try-catch.
Я вчера практиковал экзамен по Java, и я не понимаю следующий пример:
try {
try {
System.out.print("A");
throw new Exception("1");
} catch (Exception e) {
System.out.print("B");
throw new Exception("2");
} finally {
System.out.print("C");
throw new Exception("3");
}
} catch (Exception e) {
System.out.print(e.getMessage());
}
Вопрос: "Как будет выглядеть результат?"
Я был уверен, что это будет AB2C3, НО suprise suprise, это не так.
Правильный ответ - ABC3 (проверено и действительно так).
Мой вопрос: куда вышло Exception ( "2" )?
Ответы
Ответ 1
Из Спецификация языка Java 14.20.2.:
Если блок catch завершается внезапно для причины R, тогда выполняется блок finally. Тогда есть выбор:
-
Если блок finally завершается нормально, то оператор try внезапно завершается по причине R.
-
Если блок finally завершается внезапно для разума S, то оператор try внезапно завершается по причине S (и причина R отбрасывается).
Итак, когда есть блок catch, который генерирует исключение:
try {
// ...
} catch (Exception e) {
throw new Exception("2");
}
но есть также блок finally, который также генерирует исключение:
} finally {
throw new Exception("3");
}
Exception("2")
будет отброшена и будет распространяться только Exception("3")
.
Ответ 2
Исключения, сделанные в последнем блоке, подавляют исключение, ранее созданное в блоке try или catch.
Пример Java 7: http://ideone.com/0YdeZo
Из пример Javadoc:
static String readFirstLineFromFileWithFinallyBlock(String path)
throws IOException {
BufferedReader br = new BufferedReader(new FileReader(path));
try {
return br.readLine();
} finally {
if (br != null) br.close();
}
}
Однако в этом примере, если методы readLine и закрыть оба броска исключения, то метод readFirstLineFromFileWithFinallyBlock выдает исключение, выведенное из блока finally; исключение выбрасывается из блока try.
Новый синтаксис try-with
Java 7 добавляет еще один шаг подавления исключений: Исключения, сброшенные в блоке try, подавляют те, которые были выбраны ранее в try-with part.
из того же примера:
try (
java.util.zip.ZipFile zf = new java.util.zip.ZipFile(zipFileName);
java.io.BufferedWriter writer = java.nio.file.Files.newBufferedWriter(outputFilePath, charset)
) {
for (java.util.Enumeration entries = zf.entries(); entries.hasMoreElements();) {
String newLine = System.getProperty("line.separator");
String zipEntryName = ((java.util.zip.ZipEntry)entries.nextElement()).getName() + newLine;
writer.write(zipEntryName, 0, zipEntryName.length());
}
}
Исключение может быть выбрано из блока кода, связанного с try-with-resources. В приведенном выше примере исключение может быть выброшенным из блока try, и может быть выброшено до двух исключений из заявления try-with-resources, когда он пытается закрыть ZipFile и BufferedWriter. Если исключение выбрано из попробуйте блок, и одно или несколько исключений выбрасываются из try-with-resources, то те исключения, которые try-with-resources, подавляются, и исключение по блоку - это тот, который бросает writeToFileZipFileContents. Вы можете получить эти подавленные исключения путем вызова метода Throwable.getSuppressed из исключение, созданное блоком try.
В коде с вопросом каждый блок явно отбрасывает старое исключение, даже не записывая его в журнал, не хорошо, когда вы пытаетесь разрешить некоторые ошибки:
http://en.wikipedia.org/wiki/Error_hiding
Ответ 3
Так как throw new Exception("2");
выбрасывается из блока catch
, а не try
, он больше не будет пойман.
См. 14.20.2. Выполнение try-finally и try-catch-finally.
Это то, что происходит:
try {
try {
System.out.print("A"); //Prints A
throw new Exception("1");
} catch (Exception e) {
System.out.print("B"); //Caught from inner try, prints B
throw new Exception("2");
} finally {
System.out.print("C"); //Prints C (finally is always executed)
throw new Exception("3");
}
} catch (Exception e) {
System.out.print(e.getMessage()); //Prints 3 since see (very detailed) link
}
Ответ 4
Ваш вопрос очень очевиден, и ответ прост в той же степени.
Объект Exception с сообщением "2" перезаписывается объектом Exception с сообщением "3" .
Объяснение:
Когда возникает исключение, его объект он бросает, чтобы захватить блок для обработки. Но когда исключение происходит в самом блоке catch, его объект передается блоку OUTER CATCH (если есть) для обработки исключений. И тут же произошло. Объект исключения с сообщением "2" переносится в блок блокировки OUTER. Но подождите. Перед тем, как покинуть внутренний блок try-catch, он должен ВЫЙТИ НАКОНЕЦ. Здесь произошло изменение, о котором нас беспокоит. Вызывается новый объект EXCEPTION (с сообщением "3" ) или этот блок finally, который заменил уже запущенный объект Exception (с сообщением "2" ). В результате чего, когда сообщение объекта Exception напечатано, мы получили переопределенное значение, т.е. "3" , а не "2".
Keep Remember: только один объект исключения может обрабатываться блоком CATCH.
Ответ 5
Блок finally
всегда запускается. Либо вы return
из блока try, либо генерируется исключение. Исключение, созданное в блоке finally
, переопределит ту, которая была выбрана в ветки catch.
Кроме того, бросание исключения не приведет к выходу какого-либо отдельного результата. Строка throw new Exception("2");
ничего не напишет.
Ответ 6
В соответствии с вашим кодом:
try {
try {
System.out.print("A");
throw new Exception("1"); // 1
} catch (Exception e) {
System.out.print("B"); // 2
throw new Exception("2");
} finally { // 3
System.out.print("C"); // 4
throw new Exception("3");
}
} catch (Exception e) { // 5
System.out.print(e.getMessage());
}
Как вы можете видеть здесь:
- напечатать A и исключить исключение
# 1
;
- это исключение попало в catch catch и напечатало
B - # 2
;
- block finally
# 3
выполняет после try-catch (или только попробовать, если не произошло никакого исключения) оператор и выводит C - # 4
и генерирует новое исключение;
- который был захвачен внешним оператором catch
# 5
;
Результат ABC3
. И 2
опущен так же, как 1