Блок finally выполняется всегда? Ловушки JVM и System.exit()
Когда блок finally выполняется, а когда — нет
Блок finally в Java предназначен для гарантированного выполнения кода, который должен быть исполнен в любом случае: закрытие файлов, освобождение ресурсов, снятие блокировок. Однако есть три исключения, когда finally не выполняется:
- Вызов
System.exit(int)— немедленно завершает JVM. - Аварийное завершение JVM (например,
SIGKILL,OutOfMemoryErrorв критической фазе). - Бесконечный цикл или зависание в блоке
try/catch.
Во всех остальных случаях — да, finally выполняется. Давайте разберем каждый случай с примерами.
Пример 1: System.exit() — блок finally не выполняется
public class ExitExample {
public static void main(String[] args) {
try {
System.out.println("Попытка...");
System.exit(0); // JVM завершается здесь
} finally {
System.out.println("Это НЕ будет напечатано");
}
}
}
Вывод: только Попытка.... JVM убивается до того, как доходит до finally.
Пример 2: return в try — finally выполняется
public class ReturnExample {
public static int test() {
try {
return 1;
} finally {
System.out.println("Finally выполнен!");
// return 2; // если раскомментировать — переопределит return из try
}
}
public static void main(String[] args) {
System.out.println(test()); // Вывод: "Finally выполнен!" затем "1"
}
}
Важно: finally выполняется до возврата значения. Если в finally тоже есть return, он перезапишет возвращаемое значение из try.
Пример 3: Исключение в finally перезаписывает исключение из try
public class ExceptionOverrideExample {
public static void main(String[] args) {
try {
throw new RuntimeException("Ошибка в try");
} finally {
throw new IllegalArgumentException("Ошибка в finally");
}
}
}
Выбросится IllegalArgumentException. Исключение из try будет подавлено (suppressed). Это частая ошибка — терять оригинальное исключение.
Пример 4: Исключение в catch — finally все равно выполняется
public class CatchExceptionExample {
public static void main(String[] args) {
try {
int x = 1 / 0;
} catch (ArithmeticException e) {
System.out.println("Поймано: " + e.getMessage());
throw new RuntimeException("Новое исключение");
} finally {
System.out.println("Finally выполнен даже после throw в catch");
}
}
}
Вывод: сначала Поймано: / by zero, затем Finally выполнен..., затем программа упадет с RuntimeException.
Рекомендации по использованию
- Используйте
try-with-resources(Java 7+) для автоматического закрытия ресурсов — это чище, чемfinally. - В блоке
finallyникогда не бросайте исключения и не используйтеreturn— это ломает логику обработки ошибок. - Если нужно закрыть ресурс вручную, проверяйте на
nullи игнорируйте исключения закрытия:
InputStream is = null;
try {
is = new FileInputStream("file.txt");
// работа с is
} finally {
if (is != null) {
try {
is.close();
} catch (IOException ignored) {
// логируем, но не бросаем
}
}
}
Итог
Блок finally выполняется во всех случаях, кроме:
System.exit()- Аварийное завершение JVM
- Бесконечный цикл/зависание
finally выполняется даже после return и после throw, но будьте осторожны с побочными эффектами в нем.
<<Дополнение: try-with-resources как современная альтернатива
Начиная с Java 7, для работы с ресурсами, реализующими AutoCloseable, рекомендуется использовать try-with-resources. Это избавляет от ручного управления finally и делает код более безопасным.
Пример с файлом:
try (BufferedReader br = new BufferedReader(new FileReader("file.txt"))) {
String line;
while ((line = br.readLine()) != null) {
System.out.println(line);
}
} catch (IOException e) {
e.printStackTrace();
}
// Ресурс br автоматически закрыт, даже если было исключение
Преимущества:
- Код короче и читаемее.
- Исключения, возникшие при закрытии ресурса, не перезаписывают исключение из
try— они добавляются как подавленные (suppressed). - Можно использовать несколько ресурсов через точку с запятой:
try (InputStream is = new FileInputStream("a.txt");
OutputStream os = new FileOutputStream("b.txt")) {
// копирование
}
Однако помните: try-with-resources не заменяет finally для кода, который не связан с ресурсами (например, снятие блокировки, сброс счетчика). В таких случаях старый добрый finally все еще актуален.
Ответов пока нет.
Войдите чтобы оставить ответ