杰瑞科技汇

Java Swing窗口如何正确关闭?

核心概念

在 Swing 中,当你点击窗口右上角的 "X" 按钮时,会触发一个 WindowEvent,默认情况下,这个事件的处理方式取决于窗口的类型:

Java Swing窗口如何正确关闭?-图1
(图片来源网络,侵删)
  • 对于 JFrame,默认行为是 隐藏 窗口,但 不会终止 Java 应用程序,这意味着程序的后台线程(如果有的话)会继续运行,导致程序无法正常退出。
  • 对于 JDialog,默认行为也是隐藏窗口。

为了在用户关闭窗口时让程序正常退出,你必须 手动 监听这个事件并执行相应的操作。


使用 setDefaultCloseOperation() (最常用、最推荐)

这是最简单、最直接的方法,专门为 JFrame 设计,你可以在创建 JFrame 实例后,调用这个方法来设置当用户尝试关闭窗口时程序应该做什么。

JFrame 提供了几个内置的常量作为参数:

  • JFrame.EXIT_ON_CLOSE (推荐)

    • 效果:关闭窗口时,会调用 System.exit(0) 来终止整个 Java 应用程序,这是大多数独立应用程序所期望的行为。
    • 注意:此选项仅适用于 JFrame,不适用于 JDialog
  • JFrame.HIDE_ON_CLOSE (默认值)

    • 效果:关闭窗口时,窗口会隐藏(setVisible(false)),但应用程序不会退出,窗口对象仍然存在。
  • JFrame.DISPOSE_ON_CLOSE

    • 效果:关闭窗口时,窗口会被销毁(dispose()),释放其占用的系统资源,如果这是最后一个显示的窗口,应用程序可能会退出(但这不是 guaranteed)。
  • JFrame.DO_NOTHING_ON_CLOSE

    • 效果:点击 "X" 按钮时,什么都不会发生,你需要自己通过其他方式(一个“退出”按钮)来处理窗口的关闭逻辑。

示例代码

import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.SwingUtilities;
public class CloseWindowExample {
    public static void main(String[] args) {
        // 使用 SwingUtilities.invokeLater 确保 GUI 在事件调度线程中创建
        SwingUtilities.invokeLater(() -> {
            // 1. 创建 JFrame 实例
            JFrame frame = new JFrame("窗口关闭示例");
            // 2. 设置窗口大小
            frame.setSize(400, 300);
            // 3. 设置窗口关闭时的默认操作 (最关键的一步)
            // 使用 EXIT_ON_CLOSE 来确保程序完全退出
            frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
            // 4. 添加一个简单的标签
            frame.add(new JLabel("点击右上角的 'X' 按钮来关闭窗口。"));
            // 5. 设置窗口居中显示
            frame.setLocationRelativeTo(null);
            // 6. 让窗口可见
            frame.setVisible(true);
        });
    }
}

对于绝大多数 JFrame 应用程序,frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); 是你需要的全部代码。


添加 WindowListener (更灵活、适用于所有 Window)

如果你需要执行一些自定义逻辑(在关闭前保存数据、询问用户是否确认等),或者你正在处理一个 JDialog,那么你需要使用 WindowListener 接口。

WindowListener 接口有多个方法,但最常用的是 windowClosing(WindowEvent e),它会在窗口正在被关闭时被调用。

如何实现?

你可以通过以下两种方式实现 WindowListener

  1. 实现 WindowListener 接口:如果你的类已经继承了其他类,这种方式不太方便。
  2. 使用匿名内部类:这是最常见、最简洁的方式。

示例代码 (使用匿名内部类)

import javax.swing.*;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
public class WindowListenerExample {
    public static void main(String[] args) {
        SwingUtilities.invokeLater(() -> {
            JFrame frame = new JFrame("WindowListener 示例");
            frame.setSize(400, 300);
            frame.add(new JLabel("关闭窗口前会弹出确认对话框。"));
            frame.setLocationRelativeTo(null);
            // 添加 WindowListener
            frame.addWindowListener(new WindowAdapter() {
                // WindowAdapter 是一个适配器类,我们只需要重写需要的方法
                @Override
                public void windowClosing(WindowEvent e) {
                    // 在这里可以添加自定义逻辑
                    int option = JOptionPane.showConfirmDialog(
                            frame,
                            "确定要退出程序吗?",
                            "确认退出",
                            JOptionPane.YES_NO_OPTION
                    );
                    // 如果用户点击了 "是"
                    if (option == JOptionPane.YES_OPTION) {
                        // 退出程序
                        System.exit(0);
                    }
                    // 如果用户点击了 "否",我们什么都不做,窗口就不会关闭
                }
            });
            // 注意:这里不再需要 setDefaultCloseOperation
            // 因为我们自己处理了 windowClosing 事件
            // frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); 
            frame.setVisible(true);
        });
    }
}

代码解析

  1. frame.addWindowListener(...):为 JFrame 添加一个窗口事件监听器。
  2. new WindowAdapter() {...}:我们使用 WindowAdapter 而不是直接实现 WindowListenerWindowAdapter 是一个实现了 WindowListener 接口的空类,我们只需重写我们关心的方法(如 windowClosing),这样代码更简洁。
  3. windowClosing(WindowEvent e):这是核心方法,当用户点击 "X" 时触发。
  4. JOptionPane.showConfirmDialog(...):弹出一个确认对话框,让用户选择。
  5. System.exit(0):只有当用户确认后,我们才真正退出程序。

使用 Lambda 表达式 (Java 8+,更简洁)

如果你使用的是 Java 8 或更高版本,可以用 Lambda 表达式来进一步简化 WindowListener 的代码,使其更加易读。

示例代码 (使用 Lambda 表达式)

import javax.swing.*;
import java.awt.event.WindowEvent;
public class LambdaWindowListenerExample {
    public static void main(String[] args) {
        SwingUtilities.invokeLater(() -> {
            JFrame frame = new JFrame("Lambda 表达式示例");
            frame.setSize(400, 300);
            frame.add(new JLabel("使用 Lambda 表达式处理窗口关闭。"));
            frame.setLocationRelativeTo(null);
            // 使用 Lambda 表达式
            frame.addWindowListener((WindowEvent e) -> {
                System.out.println("窗口正在关闭...");
                // 直接执行退出操作
                System.exit(0);
            });
            frame.setVisible(true);
        });
    }
}

这个例子非常简单,但它展示了 Lambda 表达式如何将一个原本需要多行代码的匿名内部类简化为一行。


总结与最佳实践

方法 适用场景 优点 缺点
setDefaultCloseOperation() JFrame,只需简单退出或隐藏程序。 代码最简洁,是 Swing 的标准做法。 不适用于 JDialog,无法执行关闭前的自定义逻辑。
WindowListener 所有 Window (如 JFrame, JDialog),需要自定义关闭逻辑(如保存数据、确认退出)。 功能强大,灵活性高。 代码比 setDefaultCloseOperation 稍多。
Lambda 表达式 Java 8+,使用 WindowListener 并希望代码更简洁。 代码简洁、现代 仅限于 Java 8+ 环境。

最佳实践建议

  1. 对于 JFrame

    • 如果你的程序只有一个主窗口,并且关闭它就意味着程序结束,请直接使用 frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);,这是最清晰、最标准的方式。
    • 如果你在关闭前需要执行任何操作(如弹窗确认、保存文件),请使用 WindowListener(推荐匿名内部类或 Lambda 表达式)。
  2. 对于 JDialog

    • JDialog 没有 EXIT_ON_CLOSE 选项,你必须使用 WindowListener 来处理关闭事件,你会调用 dialog.dispose() 来关闭对话框,或者 setVisible(false) 来隐藏它。
分享:
扫描分享到社交APP
上一篇
下一篇