我有以下代码。
public static void main(String[] args)
{
if (!ArgumentsHandler.handle(args))
{
return;
}
Storage.getInstance().load();
if (!Storage.getInstance().isLoadSuccessful())
{
launch(args);
}
else
{
System.err.println("Unable to load configurations.");
}
}
我特意将if
语句中的条件转换为失败,我可以在调试器中看到它不执行launch
方法,但应用程序窗口仍在显示。
我还注意到在return
方法中使用main
语句没有效果 - 应用程序仍然继续执行。它只响应System.exit(0)
。
为什么会这样?
更新:
根据您的要求,这里有一段ArgumentsHandler。我在这里无处使用线程(至少是故意的)。
public static boolean handle(String[] args)
{
//handle args
if (args.length > 0)
{
switch (args[0])
{
//createRepository
case "-c":
configure(args);
break;
case "-r":
case "--repository":
repository(args);
break;
default:
help();
break;
}
return false;
}
return true;
}
private static void configure(String[] args)
{
if (args.length > 1)
{
boolean isRandom = false;
switch (args[1])
{
case "true":
case "1":
isRandom = true;
break;
case "false":
case "0":
//valid input, ignored
break;
default:
System.err.println("Invalid arguments. Possible values: [--configuration] [1/0].");
return;
}
Storage.configure(isRandom); //creates a bunch of json files (uses NIO).
return;
}
else
{
System.err.println("Invalid arguments. Possible values: -c [1/0].");
}
}
存储
public void load()
{
isLoadSuccessful = false;
//load configuration
app = loadConfiguration(appFilePath);
if (app == null)
{
System.err.println("Unable to load app configuration.");
return;
}
//load company
company = loadCompany(app.getCompanyFilePath());
if (company == null)
{
System.err.println("Unable to load company configuration.");
return;
}
repository = loadRepository(app.getRepositoryFilePath());
if (repository == null)
{
System.err.println("Unable to load repository configuration.");
return;
}
isLoadSuccessful = true;
}
private static App loadConfiguration(String filePath)
{
return (App) Utility.load(filePath, App.class);
}
loadConfiguration
,loadCompany
和loadRepository
真的是一样的。将来,他们不会阅读简单的json文件,但会访问复杂的档案,这就是为什么我已经创建了几个几乎相同的方法。
Utility.load
public static Object load(String path, Type type)
{
try
{
JsonReader reader = new JsonReader(new FileReader(path));
Gson gson = new Gson();
Object obj = gson.fromJson(reader, type);
reader.close();
return obj;
}
catch (IOException ex)
{
ex.printStackTrace();
return null;
}
}
只是从文件反序列化对象。
从你调用launch(args)
的方式我假设,你后来confirmed这个,main
方法是在Application
的子类。我相信这是你问题的原因。
正如您所指出的那样,有许多看似特定于JavaFX的线程正在运行。具体来说,非守护进程“JavaFX Application Thread”正在运行(至少,它是Java 10中的非守护进程)。即使main
线程退出,该线程也会使JVM保持活动状态。这是Java的正常行为:
java.lang.Thread中
当Java虚拟机启动时,通常会有一个非守护进程线程(通常调用某个指定类的名为
main
的方法)。 Java虚拟机继续执行线程,直到发生以下任一情况:
- 已经调用了
exit
类的Runtime
方法,并且安全管理器允许进行退出操作。- 通过调用
run
方法返回或抛出传播超出run
方法的异常,所有非守护程序线程的线程都已死亡。
但是,当你故意不调用Application.launch
时,为什么会启动“JavaFX应用程序线程”呢?我只是在这里猜测,但它可能与JavaFX应用程序接收的特殊处理有关。至少从Java 8开始,你不必在main
1的子类中声明一个Application
方法。如果主类是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应用程序线程上调用此方法。应用程序不得在此方法中构造Scene或Stage。应用程序可以在此方法中构造其他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分离。