如何在不杀死 JVM 的情况下停止 Java GUI

How to stop Java GUI without killing the JVM

我有一个 java 控制台应用程序,它在用户提供特定输入后打开位于单独 class 上的 GUI 应用程序。我想在关闭 GUI 后 return 返回控制台,这是通过按下 "Exit" 按钮实现的,用户可以选择执行另一项任务或重复它。我注意到用 System.exit(0) 杀死系统会杀死整个进程。我尝试使用线程来处理事情,但我对此比较陌生,而且它似乎也不起作用。有人能指出我正确的方向吗?谢谢。

我也尝试使用 frame.dispose()。它确实加载了控制台并打印出调用 System 上的主要方法时预期的内容,但不允许我输入另一个输入。

btnExit.addActionListener(new ActionListener() {
     public void actionPerformed(ActionEvent e) {
     frame.dispose();
     System.main(null);
 }
});

错误信息:Exception in thread "AWT-EventQueue-0" java.util.NoSuchElementException: No line found.

不要从侦听器方法调用 main 方法。这可能看起来具有预期的效果,但在为当前事件处理调用的方法仍在堆栈中时,事件处理线程上的 main 方法 运行ning 会被调用。这不仅会将它们的资源保留在内存中,如果您再次尝试打开 GUI,它会适得其反。

进一步注意在打开 GUI 时不要关闭任何与控制台相关的资源(System.inSystem.out),因为当您想要再次使用控制台时无法重新打开它们.

如果我没理解错的话,你的 main 方法中有一个程序流不遵循 GUI 应用程序的事件驱动流,你想 return 到那个程序流。简单 GUI 的一种方法是使用模态对话框而不是 window 或框架。

public static void main(String[] args) {
    // a simple console application
    for(boolean exit = false; !exit; ) {
        System.out.println("Enter choice:");
        System.out.println("\t1 - Say hello");
        System.out.println("\t2 - Open GUI");
        System.out.println("\t3 - Exit");
        switch(System.console().readLine()) {
            case "1": sayHello(); break;
            case "2": openGUI(); break;
            case "3": exit = true; break;
            default: System.out.println("Not a valid input");
        }
    }
}

private static void sayHello() {
    String name = System.console().readLine("Enter your name: ");
    System.out.println("Hello "+name);
}

private static void openGUI() { // a temporary GUI application
    JDialog d = new JDialog((Window)null, Dialog.ModalityType.TOOLKIT_MODAL);
    JButton b = new JButton("Exit GUI");
    b.addActionListener(ev -> d.dispose());
    d.getContentPane().add(b, BorderLayout.PAGE_END);
    d.pack();
    d.setVisible(true);
}

对于模态对话框,将打开它的方法调用不会 return 直到它被关闭。该示例使用 dispose() 来确保资源及时释放并且事件处理线程将终止,因此 main 方法可以像普通控制台应用程序一样通过简单地 returning 来终止程序。

如果对话框不是一个选项或涉及更复杂的 GUI,则让 GUI 运行 直到某个事件发生的逻辑已被概括为 SecondaryLoop class:

public static void main(String[] args) {
    for(boolean exit = false; !exit; ) {
        System.out.println("Enter choice:");
        System.out.println("\t1 - Say hello");
        System.out.println("\t2 - Open GUI");
        System.out.println("\t3 - Exit");
        switch(System.console().readLine()) {
            case "1": sayHello(); break;
            case "2": openGUI(); break;
            case "3": exit = true; break;
            default: System.out.println("Not a valid input");
        }
    }
}

private static void sayHello() {
    String name = System.console().readLine("Enter your name: ");
    System.out.println("Hello "+name);
}

private static void openGUI() {
    JFrame d = new JFrame();
    SecondaryLoop eventLoop = d.getToolkit().getSystemEventQueue().createSecondaryLoop();
    JButton b = new JButton("Exit GUI");
    b.addActionListener(ev -> {
        d.dispose();
        eventLoop.exit();
    });
    d.getContentPane().add(b, BorderLayout.PAGE_END);
    d.pack();
    d.setVisible(true);
    System.out.println("UI created");
    eventLoop.enter();
    System.out.println("UI disposed");
}

由于开启了JFrame不让主线程等待,如后面的打印语句所示,这段代码使用了SecondaryLoopenter()方法,进入一个仅当按钮的动作侦听器将调用 exit() 方法时才会结束的事件处理循环。然后,主线程 returns 并继续处理控制台循环。