Подтвердить что ты не робот

Исключение 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" )?

4b9b3361

Ответ 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