Что происходит при возникновении исключения в блоке finally в Java?

«Что происходит при возникновении исключения в блоке finally в Java?» — вопрос из категории Java Core, который задают на 10% собеседований Java Разработчик. Ниже — развёрнутый ответ с разбором ключевых моментов.

Ответ

Исключение в finally "перекрывает" исключения из try/catch блоков.

Основное поведение:

try {
    throw new RuntimeException("Ошибка в try");
} finally {
    throw new RuntimeException("Ошибка в finally");
}
// Будет выброшено: RuntimeException("Ошибка в finally")
// Оригинальное исключение из try будет подавлено (suppressed)

Механизм suppressed исключений (Java 7+):

try {
    throw new IOException("Ошибка в try");
} finally {
    RuntimeException finallyEx = new RuntimeException("Ошибка в finally");
    finallyEx.addSuppressed(e); // Явное добавление
    throw finallyEx;
}

Try-with-resources автоматически добавляет suppressed:

try (AutoCloseable resource = new MyResource()) {
    throw new IOException("Ошибка в try");
}
// Если и close(), и try блок выбрасывают исключения,
// исключение из try будет основным, а из close() - suppressed

Best Practices:

  1. Избегайте операций, которые могут выбросить исключения, в finally

  2. Обрабатывайте исключения внутри finally:

    try {
    // основной код
    } finally {
    try {
        // код очистки
    } catch (Exception e) {
        // логирование, но не проброс
        log.error("Ошибка в finally", e);
    }
    }
  3. Используйте try-with-resources для AutoCloseable ресурсов

  4. Сохраняйте оригинальное исключение:

    Exception mainException = null;
    try {
    // код, который может выбросить исключение
    } catch (Exception e) {
    mainException = e;
    throw e;
    } finally {
    try {
        // очистка
    } catch (Exception finallyEx) {
        if (mainException != null) {
            finallyEx.addSuppressed(mainException);
        }
        throw finallyEx;
    }
    }