Исходя из того, как вы звоните launch(args)
Я предполагаю, а вы позже подтвердили , что метод main
находится в подклассе Application
. Я считаю, что это является причиной вашей проблемы.
Как вы заметили, запущено много, по-видимому, специфичных для JavaFX потоков. В частности, работает не-демон «Поток приложения JavaFX» (по крайней мере, он не является демоном в Java 10). Этот поток заставит JVM остаться в живых, даже если поток main
завершится. Это нормальное поведение для Java:
java.lang.Thread
Когда запускается виртуальная машина Java, обычно существует один поток, не являющийся демоном (который обычно вызывает метод с именем main
некоторого назначенного класса). Виртуальная машина Java продолжает выполнять потоки, пока не произойдет одно из следующих событий:
- Вызван метод
exit
класса Runtime
, и диспетчер безопасности разрешил выполнение операции выхода.
- Все потоки, которые не являются потоками демонов, умерли, либо возвращаясь из вызова к методу
run
, либо выбрасывая исключение, которое распространяется за пределы метода run
.
Но почему запускается "Поток приложений JavaFX", когда вы сознательно еще не вызвали Application.launch
? Я просто догадываюсь здесь, но это, вероятно, связано со специальной обработкой, которую получают приложения JavaFX. Поскольку по крайней мере Java 8 вам не нужно объявлять метод main
внутри подкласса Application
1 . Если основной класс является подклассом Application
, Java выполняет запуск автоматически.
import javafx.application.Application;
import javafx.stage.Stage;
public class MyApp extends Application {
@Override
public void start(Stage primaryStage) throws Exception {
// create scene and show stage...
}
}
Если у вас есть вышеперечисленное и позвоните java MyApp
, приложение запустится и будет вызвано start
. Тем не менее, если у вас есть ниже:
import javafx.application.Application;
import javafx.stage.Stage;
public class MyApp extends Application {
public static void main(String[] args) {}
@Override
public void start(Stage primaryStage) throws Exception {
// create scene and show stage...
}
}
Затем вызывается метод main
, но start
- это , а не . По сути, явное объявление main
отменяет поведение по умолчанию при запуске приложения JavaFX , но не мешает инициализации среды выполнения JavaFX . Может быть, это поведение, как задумано, или, возможно, это упущение Но важно то, что это происходит только в том случае, если основной класс имеет метод main
и является подклассом Application
. Если вы разделите эти два:
public class MyApp extends Application {
// implement ...
}
public class Main {
public static void main(String[] args) {
// Perform pre-checks, return if necessary
Application.launch(MyApp.class, args);
}
}
Тогда у вас больше не будет этой проблемы.
В противном случае вы можете продолжить использование System.exit()
или переключиться на Platform.exit()
.
Есть другой, возможно, более подходящий способ справиться с этим. Похоже, вы выполняете инициализацию в методе main
перед вызовом Application.launch
. Если что-то пойдет не так во время этой инициализации, вы захотите прервать запуск приложения JavaFX. Ну, JavaFX предоставляет средства, чтобы сделать это самостоятельно: Application.init()
.
Метод инициализации приложения. Этот метод вызывается сразу после загрузки и создания класса Application. Приложение может переопределить этот метод для выполнения инициализации до фактического запуска приложения.
Реализация этого метода, предоставляемого классом Application, ничего не делает.
ПРИМЕЧАНИЕ. Этот метод не вызывается в потоке приложений JavaFX. Приложение не должно создавать сцену или сцену в этом методе. Приложение может создавать другие объекты JavaFX в этом методе.
Переместите код инициализации в этот метод. Если вы позвоните по номеру Platform.exit()
, приложение закроется, и Application.start
не будет вызвано. Альтернатива - бросить исключение внутри init
. Вы также можете получить аргументы приложения, используя Application.getParameters()
, который возвращает экземпляр Application.Parameters
.
public class MyApp extends Application {
@Override
public void init() throws Exception {
if (!ArgumentsHandler.handle(getParameters()) {
Platform.exit(); // or throw an exception
} else {
Storage storage = Storage.getInstance();
storage.load();
if (!storage.isLoadSuccessful()) {
Platform.exit(); // or throw an exception
}
}
}
@Override
public void start(Stage primaryStage) throws Exception {
// Create Scene and show the primary Stage
}
@Override
public void stop() throws Exception {
/*
* Called when the JavaFX application is exiting, such as from
* a call to Platform.exit(). Note, however, that in my experience
* this method is *not* called when Platform.exit() is called inside
* the "init" method. It is called if Platform.exit() is called from
* inside the "start" method or anywhere else in the application once
* it is properly started.
*
* This is where you could perform any necessary cleanup.
*/
}
}
1. JavaFX был включен в Java SE в версии 8. Обратите внимание, что это поведение может измениться в Java 11, поскольку JavaFX должен снова отделиться от Java SE.