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

- 对于
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:
- 实现
WindowListener接口:如果你的类已经继承了其他类,这种方式不太方便。 - 使用匿名内部类:这是最常见、最简洁的方式。
示例代码 (使用匿名内部类)
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);
});
}
}
代码解析:
frame.addWindowListener(...):为JFrame添加一个窗口事件监听器。new WindowAdapter() {...}:我们使用WindowAdapter而不是直接实现WindowListener。WindowAdapter是一个实现了WindowListener接口的空类,我们只需重写我们关心的方法(如windowClosing),这样代码更简洁。windowClosing(WindowEvent e):这是核心方法,当用户点击 "X" 时触发。JOptionPane.showConfirmDialog(...):弹出一个确认对话框,让用户选择。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+ 环境。 |
最佳实践建议:
-
对于
JFrame:- 如果你的程序只有一个主窗口,并且关闭它就意味着程序结束,请直接使用
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);,这是最清晰、最标准的方式。 - 如果你在关闭前需要执行任何操作(如弹窗确认、保存文件),请使用
WindowListener(推荐匿名内部类或 Lambda 表达式)。
- 如果你的程序只有一个主窗口,并且关闭它就意味着程序结束,请直接使用
-
对于
JDialog:JDialog没有EXIT_ON_CLOSE选项,你必须使用WindowListener来处理关闭事件,你会调用dialog.dispose()来关闭对话框,或者setVisible(false)来隐藏它。
