为啥 javafx 忽略 main 的返回并仍然启动应用程序?

Posted

技术标签:

【中文标题】为啥 javafx 忽略 main 的返回并仍然启动应用程序?【英文标题】:why javafx ignore return from main and still launches an application?为什么 javafx 忽略 main 的返回并仍然启动应用程序? 【发布时间】:2019-02-12 04:10:24 【问题描述】:

我有以下代码。

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 方法,但仍显示应用程序窗口。

我还注意到,在 main 方法中使用 return 语句没有任何效果 - 应用程序仍在继续执行。它只响应System.exit(0)

为什么会这样?

更新:

根据您的要求,这是 ArgumentsHandler 的 sn-p。我在这里没有使用线程(至少是有意的)。

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);

loadConfigurationloadCompanyloadRepository 真的是一样的。将来,他们不会读取简单的 json 文件,而是会访问复杂的档案,这就是为什么我已经创建了几个几乎相同的方法。

实用程序.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;
    

只是从文件中反序列化对象。

【问题讨论】:

能否请您出示ArgumentsHandler.handle(...)Storage.load()的代码sn-ps?我假设其中一种方法会产生一个非守护线程,它会阻止进程在离开 main 方法后结束。 main方法是从主线程调用的,你return不代表你退出了线程。您的进度不足以启动窗口,但也没有明确告诉线程退出,因此您会遇到应用程序停顿。我不确定您在 main 方法中尝试做什么,但如果您尝试使用 return,那么您可能还是想退出。 @trylimits 添加到问题中。 调试的时候可以检查是否有其他线程在运行(除了主线程)。如果您使用 Eclipse,您可以在 Debug View 中看到正在运行的线程:help.eclipse.org/photon/… 从您调用launch 的方式来看,我假设mainApplication 子类中。我发现在Application 类中包含main 会导致某种初始化,一旦main 退出,就会阻止JVM 退出。尽管没有调用launch,但仍会发生这种情况。我猜这与 Java 启动 JavaFX 应用程序的特殊方式有关,因为将 main 移动到另一个类不会导致这个问题。而且它不是由Application 类引起的,因为手动初始化该类也不会导致此问题。 【参考方案1】:

从您调用launch(args) 的方式来看,我假设以及稍后的confirmed 这个main 方法在Application 的子类中。我相信这是您的问题的原因。

正如您所注意到的,有许多看似特定于 JavaFX 的线程正在运行。具体来说,非守护程序“JavaFX 应用程序线程”正在运行(至少,它在 Java 10 中是非守护程序)。即使main 线程退出,该线程也会使JVM 保持活动状态。这是 Java 的正常行为:

java.lang.Thread

当 Java 虚拟机启动时,通常有一个非守护线程(通常调用某个指定类的名为 main 的方法)。 Java 虚拟机继续执行线程,直到发生以下任一情况:

Runtime 类的 exit 方法已被调用,安全管理器已允许执行退出操作。 不是守护线程的所有线程都已死亡,要么从对 run 方法的调用返回,要么抛出传播到 run 方法之外的异常。

但是为什么在你故意不调用Application.launch 的时候启动“JavaFX 应用程序线程”呢?我只是在这里猜测,但它可能与 JavaFX 应用程序收到的特殊处理有关。至少从 Java 8 开始,您不必在 Application1 的子类中声明 main 方法。如果主类是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()


还有另一种可能更合适的方法来处理这个问题。您似乎在调用Application.launch 之前在main 方法中执行初始化。如果在此初始化过程中出现问题,您希望中​​止启动 JavaFX 应用程序。好吧,JavaFX 自己提供了执行此操作的方法:Application.init()

应用程序初始化方法。在加载和构造 Application 类后立即调用此方法。应用程序可以覆盖此方法以在应用程序实际启动之前执行初始化。

Application 类提供的这个方法的实现什么都不做。

注意:JavaFX 应用程序线程上不会调用此方法。应用程序不得在此方法中构造场景或舞台。应用程序可以在此方法中构造其他 JavaFX 对象。

将您的初始化代码移至此方法。如果您调用Platform.exit(),则应用程序将退出并且不会调用Application.start。另一种方法是在init 内抛出异常。您还可以使用返回Application.Parameters 实例的Application.getParameters() 获取应用程序参数。

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 在版本 8 中包含在 Java SE 中。请注意,此行为在 Java 11 中可能会发生变化,因为 JavaFX 将再次与 Java SE 分离。

【讨论】:

以上是关于为啥 javafx 忽略 main 的返回并仍然启动应用程序?的主要内容,如果未能解决你的问题,请参考以下文章

Javafx怎么播放flash,就是我用Java main函数执行,直接调用Javafx做好的页面,为啥不能播放flash

为啥禁用舞台可调整大小在 javafx 中不起作用?

为啥 javafx 会破坏我的半透明游标?

为啥我在函数内部使用引用并通过引用返回它仍然有效? [复制]

与基于摇摆的图表相比,为啥基于 javafx 的图表占用更多内存

为啥包含 main 方法的类没有实例化并且在 Java 中仍然可以?