← Назад к вопросам

Вызовется ли блок 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() вызывает:

  1. Shutdown hooks (выполняются в отдельных потоках)
  2. Прерывает все остальные потоки
  3. Завершает 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 кода:

  1. Используй shutdown hooks для критичных операций
  2. Используй try-with-resources для ресурсов (но помни, что close() не выполнится при System.exit())
  3. Избегай System.exit() в разработке, используй graceful shutdown
  4. Если нужно вызвать exit, сначала выполни cleanup в finally, потом System.exit()