← Назад к вопросам
Вызовется ли блок finally если в блоке try вызвать System.exit?
1.3 Junior🔥 141 комментариев
#Основы Java
Комментарии (1)
🐱
claude-haiku-4.5PrepBro AI22 мар. 2026 г.(ред.)
Ответ сгенерирован нейросетью и может содержать ошибки
Вызовется ли блок finally если в блоке try вызвать System.exit
Ответ: НЕТ, блок finally НЕ будет вызван. System.exit() немедленно завершает JVM, не выполняя никакие finally блоки.
Как работает System.exit()
SYSTEM.exit() вызывает shutdown hook и прерывает все потоки без выполнения finally блоков:
public class SystemExitExample {
public static void main(String[] args) {
try {
System.out.println("1. В блоке try");
System.exit(0); // JVM прерывается здесь
System.out.println("Это не выведется");
} catch (Exception e) {
System.out.println("2. В блоке catch");
} finally {
System.out.println("3. В блоке finally"); // НИКОГДА НЕ ВЫВЕДЕТСЯ
}
System.out.println("4. После try-catch-finally"); // НЕ ВЫВЕДЕТСЯ
}
}
// Вывод:
// 1. В блоке try
Различные коды выхода
public class ExitCodesExample {
public static void main(String[] args) {
try {
// Код выхода 0 = успешное завершение
System.exit(0);
} finally {
System.out.println("finally"); // НЕ ВЫВЕДЕТСЯ
}
}
}
public class ExitWithError {
public static void main(String[] args) {
try {
// Ненулевой код = ошибка
System.exit(1);
} finally {
System.out.println("finally"); // НЕ ВЫВЕДЕТСЯ
}
}
}
Почему finally не вызывается
System.exit() вызывает:
- Shutdown hooks (выполняются в отдельных потоках)
- Прерывает все остальные потоки
- Завершает JVM
Finally блоки — это особенность компилятора Java, а не JVM. Когда JVM завершается, код не выполняется.
Если нужно гарантировать выполнение кода
Вариант 1: Использовать shutdown hook
public class ShutdownHookExample {
public static void main(String[] args) {
// Регистрируем shutdown hook
Runtime.getRuntime().addShutdownHook(new Thread(() -> {
System.out.println("Cleanup: закрытие ресурсов");
// Это БУДЕТ вызвано при System.exit()
}));
try {
System.out.println("Работаю...");
System.exit(0); // Shutdown hook выполнится
} finally {
System.out.println("finally"); // НЕ ВЫВЕДЕТСЯ
}
}
}
// Вывод:
// Работаю...
// Cleanup: закрытие ресурсов
Вариант 2: Try-with-resources (для AutoCloseable)
public class TryWithResourcesExample {
static class MyResource implements AutoCloseable {
@Override
public void close() {
System.out.println("Закрытие ресурса");
// Это НЕ будет вызвано при System.exit()
}
}
public static void main(String[] args) {
try (MyResource resource = new MyResource()) {
System.out.println("Использую ресурс");
System.exit(0);
}
// close() тоже не вызовется
}
}
// Вывод:
// Использую ресурс
Вариант 3: Не использовать System.exit(), а вернуть код ошибки
public class ReturnCodeExample {
public static void main(String[] args) {
int exitCode = 0;
try {
System.out.println("Работаю...");
// Вместо System.exit(0)
exitCode = 0; // просто устанавливаем код
} catch (Exception e) {
System.out.println("Ошибка");
exitCode = 1;
} finally {
System.out.println("finally БУДЕТ вызван!");
// Закрываем ресурсы
}
System.exit(exitCode); // exit в конце, finally уже выполнен
}
}
// Вывод:
// Работаю...
// finally БУДЕТ вызван!
Специальные случаи
System.exit(0) в одном потоке:
public class MultiThreadExample {
public static void main(String[] args) {
new Thread(() -> {
try {
System.out.println("Поток 1: работаю");
Thread.sleep(1000);
System.out.println("Поток 1: готов");
} catch (InterruptedException e) {
System.out.println("Поток 1: прерван");
} finally {
System.out.println("Поток 1: finally");
}
}).start();
try {
System.out.println("Main: работаю");
Thread.sleep(100);
System.exit(0); // main поток вызывает exit
} finally {
System.out.println("Main: finally");
}
}
}
// Вывод примерно:
// Main: работаю
// Поток 1: работаю
// Ни Main finally, ни Поток 1 finally не выведутся
Как заставить finally выполниться
public class FinallyGuaranteedExample {
public static void main(String[] args) {
ProcessBuilder pb = new ProcessBuilder();
try {
System.out.println("Работаю");
// Запустить новый процесс вместо System.exit()
// или использовать другой механизм
} finally {
System.out.println("finally ВЫВЕДЕТСЯ!");
}
// System.exit() только в конце
System.exit(0);
}
}
Практический пример: безопасное завершение
public class SafeShutdown {
private static volatile boolean running = true;
public static void main(String[] args) {
// Регистрируем hook для graceful shutdown
Runtime.getRuntime().addShutdownHook(new Thread(() -> {
System.out.println("Shutdown signal received");
running = false;
System.out.println("Cleanup completed");
}));
try {
while (running) {
System.out.println("Processing...");
Thread.sleep(1000);
}
} catch (InterruptedException e) {
System.out.println("Interrupted");
} finally {
System.out.println("Finally block");
}
System.out.println("Normal exit");
}
}
Таблица поведения
| Сценарий | finally вызывается? |
|---|---|
| Нормальный return | ДА |
| throw Exception | ДА |
| System.exit() | НЕТ |
| JVM crash (SIGSEGV) | НЕТ |
| Runtime.halt() | НЕТ |
| Ctrl+C (SIGTERM) | ДА (если есть shutdown hook) |
| Out of memory | МОЖЕТ |
Вывод
System.exit() напрямую завершает JVM, поэтому finally блоки не выполняются. Если нужно гарантировать выполнение cleanup кода:
- Используй shutdown hooks для критичных операций
- Используй try-with-resources для ресурсов (но помни, что close() не выполнится при System.exit())
- Избегай System.exit() в разработке, используй graceful shutdown
- Если нужно вызвать exit, сначала выполни cleanup в finally, потом System.exit()