2013-08-07 2 views
166

У меня есть небольшая теоретическая проблема с конструкциями try-catch.Исключение Java не поймано?

вчера я взял практический экзамен о 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 (проверено и действительно так).

Вопрос, где произошло Исключение («2»)?

+8

+1 Ahh man, я знал этот ответ. Об этом меня попросили в интервью. Это очень хороший вопрос для понимания того, как try/catch/наконец работает в стеке. –

+10

Существует только один оператор печати, который может печатать число (последнее: 'print (e.getMessage())). Вы считали, что вывод будет 'AB2C3': вы считали, что внешний блок' catch' будет выполнен дважды? –

+0

В java, перед выполнением команды, выполняющей управление передачей из блока catch, блок finally выполняется при условии, что он существует. Если только код в блоке finally не передает управление наружу, выполняется отложенная команда из блока catch. – Thomas

ответ

192

С Java Language Specification 14.20.2.:

Если блок catch завершается внезапно по причине R, то выполняется блок finally. Тогда есть выбор:

  • Если, наконец, блок завершается нормально, тогда оператор попытка завершается преждевременно по причине R.

  • Если, наконец, блок завершается преждевременно по причине S, то попытка заявление завершается внезапно по причине S (и причина R отбрасывается).

Итак, если есть подвох блок, который генерирует исключение:

try { 
    // ... 
} catch (Exception e) { 
    throw new Exception("2"); 
} 

но есть и, наконец, блок, который также бросает исключение:

} finally { 
    throw new Exception("3"); 
} 

Exception("2") будут отброшены и будут распространяться только Exception("3").

+71

Это справедливо даже для операторов 'return'. Если ваш блок finally имеет возврат, он переопределит любой возврат в блоке 'try' или' catch'. Из-за этих «особенностей» хорошей практикой является то, что, наконец, блок должен ** никогда не вызывать исключение или иметь оператор возврата. – Augusto

+0

Это также преимущество наследования у try-in-resources в Java 7. Оно сохраняет начальное исключение, если при закрытии ресурсов генерируется вторичное исключение, что упрощает процедуру отладки. – w25r

19

Исключения, выброшенные в блок finally, подавляют исключение, ранее созданное в try или catch block.

Java 7 Пример: http://ideone.com/0YdeZo

От Javadoc's Пример:


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 (
     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, а до двух исключений может быть сброшено из оператора try-with-resources, когда он пытается закрыть объекты ZipFile и BufferedWriter . Если исключение выбрано из блока try , и одно или несколько исключений выбрасываются из оператора try-with-resources , то те исключения, которые выбраны из инструкции try-with-resources , подавляются, и исключение, вызванное block - это тот, который вызывается методом writeToFileZipFileContents . Вы можете получить эти запрещенные исключения , вызвав метод Throwable.getSuppressed из исключения , созданного блоком try.


В коде от вопроса, каждый блок явно отбрасывая старое исключение, даже не его регистрации, не хорошо, когда вы пытаетесь решить некоторые ошибки:

http://en.wikipedia.org/wiki/Error_hiding

9

С throw new Exception("2"); сбрасывается с catch блок, а не try, он больше не будет поймать.
См. 14.20.2. Execution of try-finally and 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 
} 
+0

Да, это так, я вижу, что это происходит, но я искал объяснения - почему он так себя ведет – Kousalik

2

finally блок всегда работает. Либо вы получаете return из блока try, либо генерируется исключение. Исключение, заброшенное в блоке finally, переопределит ту, которая была выбрана в ветви catch.

Кроме того, выброс исключения не приведет к выходу из строя. Строка throw new Exception("2"); ничего не напишет.

+1

Да, я знаю, что выбрасывание Исключение ничего не выводит сам по себе, но я не видел причины, почему Исключение 2 следует отбросить , Я немного умнее снова :-) – Kousalik

+0

всегда очень долгое время, и в очень долгое время все может случиться (проверьте головоломку http://wouter.coekaerts.be/2012/puzzle-dreams) – Dainius

4

Ваш вопрос очень очевиден, и ответ прост в той же степени. Объект Exception с сообщением «2» перезаписывается объектом Exception с сообщением «3».

Объяснение: Когда исключение происходит, его объект он выброшен, чтобы поймать блок для обработки. Но когда исключение происходит в самом блоке catch, его объект передается блоку OUTER CATCH (если есть) для обработки исключений. И тут же произошло. Объект исключения с сообщением «2» переносится в блок блокировки OUTER. Но ждите ..Перед тем как покинуть внутренний блок try-catch, он ДОЛЖЕН ВЫПОЛНЕН. Здесь произошло изменение, о котором нас беспокоит. Вызывается новый объект EXCEPTION (с сообщением «3») или этот блок finally, который заменил уже запущенный объект Exception (с сообщением «2»). В результате чего, когда сообщение объекта Exception напечатано, мы получили переопределенное значение, т.е. «3», а не «2».

Keep Remember: Только один объект исключения может обрабатываться блоком CATCH.

0

По вашему коду:

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

Как вы можете увидеть здесь:

  1. печати A и выбрасывает исключение # 1;
  2. это исключение попало в ловушку и распечатало B - # 2;
  3. блок finally # 3 выполняет после try-catch (или только попробуйте, если не произошло никакого исключения) заявление и распечатки C - # 4 и выбрасывает новое исключение;
  4. этот зарегистрирован по выписке внешнего вылова # 5;

Результат: ABC3. И 2 опущен точно так же, как 1

+0

Извините, исключение (" 1 ") не пропущен, но успешно пойман –

+0

@ Черная Мэгги. Он кэшируется и генерирует новое исключение => это не кэшируется, а программа завершается. И прежде, чем этот блок, наконец, будет выполнен. –

Смежные вопросы