第一部分:Java GUI 概述

1.1 Java GUI 技术发展史

技术 年代 特点 现状
AWT JDK 1.0 依赖本地操作系统组件,重量级 基本废弃,但部分概念保留
Swing JDK 1.2 纯 Java 实现,轻量级,可移植 主流,仍在大量使用
JavaFX JDK 8 引入 现代化 GUI,支持 CSS、FXML JDK 11 起从 JDK 移除,独立维护

1.2 本文范围

  • 主要讲解 Swing(最常用,面试和企业项目主流)

  • 补充 AWT 的基础概念(事件模型)

  • 简要介绍 JavaFX

第二部分:AWT 基础

2.1 AWT 核心类

text

java.awt 包
├── Component(所有组件的父类)
│   ├── Button
│   ├── Label
│   ├── TextComponent
│   │   ├── TextField
│   │   └── TextArea
│   ├── Checkbox
│   ├── Choice
│   └── Container(容器)
│       ├── Panel
│       ├── Frame(窗口)
│       └── Dialog
├── LayoutManager(布局管理器)
│   ├── FlowLayout
│   ├── BorderLayout
│   ├── GridLayout
│   └── CardLayout
└── Event(事件)
    ├── ActionEvent
    ├── MouseEvent
    └── KeyEvent

2.2 AWT 示例

java

import java.awt.*;
import java.awt.event.*;

public class AWTExample {
    public static void main(String[] args) {
        // 创建窗口
        Frame frame = new Frame("AWT 示例");
        frame.setSize(300, 200);
        frame.setLayout(new FlowLayout());
        
        // 创建按钮
        Button btn = new Button("点击我");
        
        // 添加事件监听
        btn.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                System.out.println("按钮被点击了");
            }
        });
        
        frame.add(btn);
        frame.setVisible(true);
        
        // 窗口关闭事件(AWT 需要手动处理)
        frame.addWindowListener(new WindowAdapter() {
            @Override
            public void windowClosing(WindowEvent e) {
                System.exit(0);
            }
        });
    }
}

第三部分:Swing 核心知识点

3.1 Swing 架构

text

javax.swing 包
├── JComponent(所有 Swing 组件的父类,继承自 AWT 的 Container)
│   ├── JButton(按钮)
│   ├── JLabel(标签)
│   ├── JTextField(单行文本)
│   ├── JTextArea(多行文本)
│   ├── JCheckBox(复选框)
│   ├── JRadioButton(单选按钮)
│   ├── JComboBox(下拉列表)
│   ├── JList(列表)
│   ├── JTable(表格)
│   ├── JTree(树形结构)
│   ├── JMenuBar(菜单栏)
│   ├── JPanel(面板,容器)
│   ├── JScrollPane(滚动面板)
│   └── JTabbedPane(选项卡面板)
└── JFrame(顶级窗口,继承自 AWT 的 Frame)

3.2 Swing 特点

  • 轻量级组件:纯 Java 实现,不依赖本地系统

  • 可插拔外观(Pluggable Look and Feel):可以切换不同操作系统风格

  • MVC 架构:Model-View-Controller 设计模式

  • 双缓冲技术:减少闪烁,提升绘制性能

第四部分:Swing 核心组件详解

4.1 JFrame(顶级窗口)

基本用法

java

import javax.swing.*;

public class JFrameDemo {
    public static void main(String[] args) {
        // 1. 创建窗口
        JFrame frame = new JFrame("我的第一个 Swing 窗口");
        
        // 2. 设置窗口大小
        frame.setSize(400, 300);
        
        // 3. 设置关闭操作(点击 X 时退出程序)
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        
        // 4. 设置窗口位置居中
        frame.setLocationRelativeTo(null);
        
        // 5. 设置布局管理器
        frame.setLayout(null);  // 绝对布局(不推荐)
        
        // 6. 设置窗口可见(必须最后执行)
        frame.setVisible(true);
        
        // 7. 其他设置
        frame.setResizable(false);  // 禁止调整大小
        frame.setAlwaysOnTop(true); // 置顶
    }
}
JFrame 常用方法

java

frame.setTitle("标题");              // 设置标题
frame.setSize(800, 600);             // 设置宽高(像素)
frame.setLocation(100, 50);          // 设置位置(x, y)
frame.setLocationRelativeTo(null);   // 居中显示
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);  // 关闭时退出
frame.setResizable(false);           // 禁止调整窗口大小
frame.setAlwaysOnTop(true);          // 窗口置顶
frame.setIconImage(image);           // 设置图标
frame.setVisible(true);              // 显示窗口(最后调用)
frame.pack();                        // 根据组件自动调整窗口大小
关闭操作常量

java

JFrame.EXIT_ON_CLOSE          // 退出程序(最常用)
JFrame.DISPOSE_ON_CLOSE       // 释放窗口资源,但不退出程序
JFrame.HIDE_ON_CLOSE          // 隐藏窗口(默认)
JFrame.DO_NOTHING_ON_CLOSE    // 什么都不做

4.2 JPanel(面板/容器)

特点
  • 作为中间容器,用于组织其他组件

  • 可以设置布局管理器

  • 可以嵌套使用

代码示例

java

import javax.swing.*;
import java.awt.*;

public class JPanelDemo {
    public static void main(String[] args) {
        JFrame frame = new JFrame("面板示例");
        frame.setSize(500, 400);
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.setLayout(new BorderLayout());
        
        // 创建三个面板
        JPanel topPanel = new JPanel();
        topPanel.setBackground(Color.BLUE);
        topPanel.add(new JLabel("顶部面板"));
        
        JPanel centerPanel = new JPanel();
        centerPanel.setBackground(Color.GREEN);
        centerPanel.setLayout(new FlowLayout());
        centerPanel.add(new JButton("按钮1"));
        centerPanel.add(new JButton("按钮2"));
        centerPanel.add(new JButton("按钮3"));
        
        JPanel bottomPanel = new JPanel();
        bottomPanel.setBackground(Color.RED);
        bottomPanel.add(new JLabel("底部面板"));
        
        // 添加到框架
        frame.add(topPanel, BorderLayout.NORTH);
        frame.add(centerPanel, BorderLayout.CENTER);
        frame.add(bottomPanel, BorderLayout.SOUTH);
        
        frame.setVisible(true);
    }
}

4.3 常用组件

JLabel(标签)

java

JLabel label = new JLabel("用户名:");
JLabel label2 = new JLabel("带图标", SwingConstants.CENTER);
label2.setFont(new Font("宋体", Font.BOLD, 20));
label2.setForeground(Color.RED);
JTextField(单行文本框)

java

JTextField textField = new JTextField();           // 默认
JTextField textField2 = new JTextField(20);        // 指定列数
JTextField textField3 = new JTextField("默认文字", 15);
textField.setText("设置文本");
String text = textField.getText();                 // 获取文本
textField.setEditable(false);                      // 设为只读
JPasswordField(密码框)

java

JPasswordField pwdField = new JPasswordField(15);
pwdField.setEchoChar('*');                         // 设置回显字符
char[] pwd = pwdField.getPassword();               // 获取密码(char数组)
// 注意:不要用 String 存储密码,用 char[] 更安全
JTextArea(多行文本域)

java

JTextArea textArea = new JTextArea(10, 30);        // 行数,列数
textArea.setLineWrap(true);                        // 自动换行
textArea.setWrapStyleWord(true);                   // 按单词换行
textArea.setText("初始文本");
String content = textArea.getText();

// 放入滚动面板
JScrollPane scrollPane = new JScrollPane(textArea);
JButton(按钮)

java

JButton btn1 = new JButton("确定");
JButton btn2 = new JButton("带图标", new ImageIcon("icon.png"));
btn1.setToolTipText("点击此按钮提交");              // 提示文本
btn1.setEnabled(false);                            // 禁用按钮
btn1.setMnemonic('O');                             // 快捷键 Alt+O
JCheckBox(复选框)

java

JCheckBox check1 = new JCheckBox("Java");
JCheckBox check2 = new JCheckBox("Python", true);  // 默认选中
check1.isSelected();                               // 判断是否选中
JRadioButton(单选按钮)

java

// 必须放在 ButtonGroup 中才能实现互斥
JRadioButton radio1 = new JRadioButton("男");
JRadioButton radio2 = new JRadioButton("女", true); // 默认选中
ButtonGroup group = new ButtonGroup();
group.add(radio1);
group.add(radio2);
JComboBox(下拉列表)

java

String[] items = {"选项1", "选项2", "选项3"};
JComboBox<String> comboBox = new JComboBox<>(items);
comboBox.addItem("选项4");                         // 添加选项
comboBox.removeItem("选项2");                      // 移除选项
int index = comboBox.getSelectedIndex();           // 获取选中索引
String item = (String) comboBox.getSelectedItem(); // 获取选中值
JList(列表)

java

String[] data = {"项目A", "项目B", "项目C", "项目D"};
JList<String> list = new JList<>(data);
list.setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);
JScrollPane scrollPane = new JScrollPane(list);   // 需要滚动面板

// 获取选中值
List<String> selected = list.getSelectedValuesList();
JTable(表格)

java

// 数据:二维数组
String[][] data = {
    {"1", "张三", "男", "20"},
    {"2", "李四", "女", "22"},
    {"3", "王五", "男", "21"}
};
String[] columns = {"编号", "姓名", "性别", "年龄"};

JTable table = new JTable(data, columns);
table.setRowHeight(25);                            // 设置行高
JScrollPane scrollPane = new JScrollPane(table);  // 必须加滚动面板

第五部分:布局管理器(LayoutManager)

5.1 概述

布局管理器负责自动排列容器中的组件,避免硬编码坐标。

5.2 FlowLayout(流式布局)

  • 从左到右排列,放不下就换行

  • 默认居中对齐

java

JPanel panel = new JPanel(new FlowLayout());
// 或
JPanel panel = new JPanel();
panel.setLayout(new FlowLayout(FlowLayout.LEFT, 10, 10)); // 左对齐,间距10

panel.add(new JButton("按钮1"));
panel.add(new JButton("按钮2"));
panel.add(new JButton("按钮3"));

5.3 BorderLayout(边界布局)

  • 分为五个区域:NORTH、SOUTH、WEST、EAST、CENTER

  • JFrame 默认使用 BorderLayout

java

JFrame frame = new JFrame();
frame.setLayout(new BorderLayout(10, 10));  // 水平间距,垂直间距

frame.add(new JButton("北"), BorderLayout.NORTH);
frame.add(new JButton("南"), BorderLayout.SOUTH);
frame.add(new JButton("西"), BorderLayout.WEST);
frame.add(new JButton("东"), BorderLayout.EAST);
frame.add(new JButton("中"), BorderLayout.CENTER);

5.4 GridLayout(网格布局)

  • 将容器划分为等大的网格

  • 每个组件占一个格子

java

JPanel panel = new JPanel(new GridLayout(3, 2, 5, 5)); // 3行2列,间距5

for (int i = 1; i <= 6; i++) {
    panel.add(new JButton("按钮" + i));
}

5.5 CardLayout(卡片布局)

  • 多个组件重叠,一次只显示一个

  • 常用于选项卡效果

java

JPanel panel = new JPanel();
CardLayout cardLayout = new CardLayout();
panel.setLayout(cardLayout);

panel.add(new JLabel("页面1"), "page1");
panel.add(new JLabel("页面2"), "page2");
panel.add(new JLabel("页面3"), "page3");

// 切换页面
cardLayout.show(panel, "page2");
cardLayout.next(panel);
cardLayout.previous(panel);

5.6 BoxLayout(箱式布局)

  • 水平或垂直排列组件

java

JPanel panel = new JPanel();
panel.setLayout(new BoxLayout(panel, BoxLayout.Y_AXIS));  // 垂直排列

panel.add(new JButton("按钮1"));
panel.add(Box.createVerticalStrut(10));  // 添加间距
panel.add(new JButton("按钮2"));

5.7 布局管理器对比表

布局 特点 适用场景
FlowLayout 从左到右,自动换行 工具栏、按钮组
BorderLayout 五区域 主窗口布局
GridLayout 等分网格 计算器、表单
CardLayout 重叠切换 选项卡、向导
BoxLayout 水平/垂直排列 表单、面板
GridBagLayout 最灵活最复杂 复杂专业界面

第六部分:事件处理(Event Handling)

6.1 事件模型(委托事件模型)

text

事件源(组件) → 触发事件 → 事件对象 → 事件监听器 → 执行处理

6.2 核心接口

  • 事件源:发生事件的组件(如 JButton)

  • 事件对象:封装事件信息(如 ActionEvent)

  • 事件监听器:处理事件的接口(如 ActionListener)

6.3 ActionListener(动作事件)

java

import javax.swing.*;
import java.awt.event.*;

public class ActionListenerDemo {
    public static void main(String[] args) {
        JFrame frame = new JFrame("事件示例");
        frame.setSize(300, 200);
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.setLayout(new FlowLayout());
        
        JButton btn = new JButton("点击我");
        JLabel label = new JLabel("等待点击...");
        
        // 方式1:匿名内部类(最常用)
        btn.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                label.setText("按钮被点击了!");
                System.out.println("点击事件触发");
            }
        });
        
        // 方式2:Lambda 表达式(JDK 8+)
        btn.addActionListener(e -> {
            label.setText("Lambda 方式");
        });
        
        // 方式3:独立监听器类
        btn.addActionListener(new MyActionListener());
        
        frame.add(btn);
        frame.add(label);
        frame.setVisible(true);
    }
}

// 独立监听器类
class MyActionListener implements ActionListener {
    @Override
    public void actionPerformed(ActionEvent e) {
        System.out.println("独立的监听器类");
    }
}

6.4 MouseListener(鼠标事件)

java

import javax.swing.*;
import java.awt.event.*;

public class MouseListenerDemo {
    public static void main(String[] args) {
        JFrame frame = new JFrame("鼠标事件");
        frame.setSize(300, 200);
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        
        JPanel panel = new JPanel();
        panel.setBackground(Color.LIGHT_GRAY);
        
        // 鼠标监听
        panel.addMouseListener(new MouseListener() {
            @Override
            public void mouseClicked(MouseEvent e) {
                System.out.println("鼠标单击:(" + e.getX() + ", " + e.getY() + ")");
            }
            
            @Override
            public void mousePressed(MouseEvent e) {
                System.out.println("鼠标按下");
            }
            
            @Override
            public void mouseReleased(MouseEvent e) {
                System.out.println("鼠标释放");
            }
            
            @Override
            public void mouseEntered(MouseEvent e) {
                System.out.println("鼠标进入");
            }
            
            @Override
            public void mouseExited(MouseEvent e) {
                System.out.println("鼠标离开");
            }
        });
        
        // 或使用适配器类(只需要重写需要的方法)
        panel.addMouseListener(new MouseAdapter() {
            @Override
            public void mouseClicked(MouseEvent e) {
                if (e.getButton() == MouseEvent.BUTTON1) {
                    System.out.println("左键单击");
                } else if (e.getButton() == MouseEvent.BUTTON3) {
                    System.out.println("右键单击");
                }
            }
        });
        
        frame.add(panel);
        frame.setVisible(true);
    }
}

6.5 KeyListener(键盘事件)

java

JTextField textField = new JTextField(20);

textField.addKeyListener(new KeyAdapter() {
    @Override
    public void keyPressed(KeyEvent e) {
        if (e.getKeyCode() == KeyEvent.VK_ENTER) {
            System.out.println("按了回车键");
        }
        System.out.println("按下的键:" + e.getKeyChar());
    }
});

6.6 ItemListener(选项事件 - 复选框/下拉列表)

java

JCheckBox checkBox = new JCheckBox("启用功能");

checkBox.addItemListener(new ItemListener() {
    @Override
    public void itemStateChanged(ItemEvent e) {
        if (e.getStateChange() == ItemEvent.SELECTED) {
            System.out.println("选中");
        } else {
            System.out.println("取消选中");
        }
    }
});

// Lambda 方式
checkBox.addItemListener(e -> {
    boolean selected = (e.getStateChange() == ItemEvent.SELECTED);
    System.out.println("状态:" + selected);
});

6.7 DocumentListener(文本变化事件)

java

JTextField textField = new JTextField(20);

textField.getDocument().addDocumentListener(new DocumentListener() {
    @Override
    public void insertUpdate(DocumentEvent e) {
        System.out.println("文本插入:" + textField.getText());
    }
    
    @Override
    public void removeUpdate(DocumentEvent e) {
        System.out.println("文本删除:" + textField.getText());
    }
    
    @Override
    public void changedUpdate(DocumentEvent e) {
        // 属性变化(不常用)
    }
});

6.8 事件监听器总结

监听器 事件类 适用组件
ActionListener ActionEvent JButton、JMenuItem、JTextField
MouseListener MouseEvent 所有 Component
MouseMotionListener MouseEvent 所有 Component(拖拽、移动)
KeyListener KeyEvent 所有 Component
ItemListener ItemEvent JCheckBox、JRadioButton、JComboBox
DocumentListener DocumentEvent JTextField、JTextArea
WindowListener WindowEvent JFrame、JDialog
FocusListener FocusEvent 所有 Component

第七部分:完整综合示例(登录界面)

java

import javax.swing.*;
import java.awt.*;
import java.awt.event.*;

public class LoginDemo extends JFrame {
    private JTextField usernameField;
    private JPasswordField passwordField;
    private JLabel messageLabel;
    
    public LoginDemo() {
        setTitle("用户登录系统");
        setSize(400, 250);
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        setLocationRelativeTo(null);
        setResizable(false);
        
        // 创建主面板
        JPanel mainPanel = new JPanel(new BorderLayout(10, 10));
        mainPanel.setBorder(BorderFactory.createEmptyBorder(20, 20, 20, 20));
        
        // 标题
        JLabel titleLabel = new JLabel("用户登录", SwingConstants.CENTER);
        titleLabel.setFont(new Font("微软雅黑", Font.BOLD, 24));
        titleLabel.setForeground(new Color(0, 102, 204));
        
        // 表单面板
        JPanel formPanel = new JPanel(new GridBagLayout());
        GridBagConstraints gbc = new GridBagConstraints();
        gbc.insets = new Insets(5, 5, 5, 5);
        
        // 用户名
        gbc.gridx = 0;
        gbc.gridy = 0;
        formPanel.add(new JLabel("用户名:"), gbc);
        
        gbc.gridx = 1;
        gbc.gridy = 0;
        usernameField = new JTextField(15);
        formPanel.add(usernameField, gbc);
        
        // 密码
        gbc.gridx = 0;
        gbc.gridy = 1;
        formPanel.add(new JLabel("密 码:"), gbc);
        
        gbc.gridx = 1;
        gbc.gridy = 1;
        passwordField = new JPasswordField(15);
        passwordField.setEchoChar('●');
        formPanel.add(passwordField, gbc);
        
        // 按钮面板
        JPanel buttonPanel = new JPanel(new FlowLayout(FlowLayout.CENTER, 20, 0));
        JButton loginBtn = new JButton("登录");
        JButton resetBtn = new JButton("重置");
        JButton exitBtn = new JButton("退出");
        
        // 设置按钮颜色
        loginBtn.setBackground(new Color(0, 153, 204));
        loginBtn.setForeground(Color.WHITE);
        loginBtn.setFocusPainted(false);
        
        buttonPanel.add(loginBtn);
        buttonPanel.add(resetBtn);
        buttonPanel.add(exitBtn);
        
        // 消息标签
        messageLabel = new JLabel("请输入用户名和密码", SwingConstants.CENTER);
        messageLabel.setForeground(Color.GRAY);
        messageLabel.setFont(new Font("宋体", Font.PLAIN, 12));
        
        // 组装
        mainPanel.add(titleLabel, BorderLayout.NORTH);
        mainPanel.add(formPanel, BorderLayout.CENTER);
        mainPanel.add(buttonPanel, BorderLayout.SOUTH);
        
        add(mainPanel, BorderLayout.CENTER);
        add(messageLabel, BorderLayout.SOUTH);
        
        // ============ 事件监听 ============
        
        // 登录按钮
        loginBtn.addActionListener(e -> handleLogin());
        
        // 回车键登录
        usernameField.addActionListener(e -> handleLogin());
        passwordField.addActionListener(e -> handleLogin());
        
        // 重置按钮
        resetBtn.addActionListener(e -> {
            usernameField.setText("");
            passwordField.setText("");
            messageLabel.setText("已重置");
            messageLabel.setForeground(Color.GRAY);
            usernameField.requestFocus();
        });
        
        // 退出按钮
        exitBtn.addActionListener(e -> System.exit(0));
        
        // 窗口关闭确认
        addWindowListener(new WindowAdapter() {
            @Override
            public void windowClosing(WindowEvent e) {
                int choice = JOptionPane.showConfirmDialog(
                    LoginDemo.this,
                    "确定要退出吗?",
                    "确认退出",
                    JOptionPane.YES_NO_OPTION
                );
                if (choice == JOptionPane.YES_OPTION) {
                    System.exit(0);
                }
            }
        });
        
        setVisible(true);
    }
    
    private void handleLogin() {
        String username = usernameField.getText().trim();
        String password = new String(passwordField.getPassword());
        
        if (username.isEmpty() || password.isEmpty()) {
            messageLabel.setForeground(Color.RED);
            messageLabel.setText("用户名和密码不能为空!");
            return;
        }
        
        // 模拟验证
        if ("admin".equals(username) && "123456".equals(password)) {
            messageLabel.setForeground(new Color(0, 153, 0));
            messageLabel.setText("登录成功!欢迎 " + username);
            JOptionPane.showMessageDialog(
                this,
                "登录成功!",
                "提示",
                JOptionPane.INFORMATION_MESSAGE
            );
        } else {
            messageLabel.setForeground(Color.RED);
            messageLabel.setText("用户名或密码错误!");
            passwordField.setText("");
            passwordField.requestFocus();
        }
    }
    
    public static void main(String[] args) {
        // 设置 Swing 外观为系统风格
        try {
            UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
        } catch (Exception e) {
            e.printStackTrace();
        }
        
        // 在事件调度线程中创建 GUI
        SwingUtilities.invokeLater(() -> new LoginDemo());
    }
}

第八部分:Swing 高级特性

8.1 对话框(JOptionPane)

java

// 消息对话框
JOptionPane.showMessageDialog(parent, "这是一个消息");
JOptionPane.showMessageDialog(parent, "带标题", "标题", JOptionPane.INFORMATION_MESSAGE);

// 确认对话框
int result = JOptionPane.showConfirmDialog(parent, "确认删除吗?");
if (result == JOptionPane.YES_OPTION) {
    System.out.println("用户点击了是");
} else if (result == JOptionPane.NO_OPTION) {
    System.out.println("用户点击了否");
}

// 输入对话框
String input = JOptionPane.showInputDialog(parent, "请输入姓名:");
if (input != null && !input.isEmpty()) {
    System.out.println("输入的姓名:" + input);
}

// 选项对话框
String[] options = {"选项A", "选项B", "选项C"};
int choice = JOptionPane.showOptionDialog(
    parent,
    "请选择一个选项:",
    "选择",
    JOptionPane.DEFAULT_OPTION,
    JOptionPane.QUESTION_MESSAGE,
    null,
    options,
    options[0]
);

8.2 菜单(JMenuBar)

java

JMenuBar menuBar = new JMenuBar();

// 文件菜单
JMenu fileMenu = new JMenu("文件");
JMenuItem newItem = new JMenuItem("新建");
JMenuItem openItem = new JMenuItem("打开");
JMenuItem saveItem = new JMenuItem("保存");
JMenuItem exitItem = new JMenuItem("退出");

// 快捷键
newItem.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_N, InputEvent.CTRL_DOWN_MASK));
openItem.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_O, InputEvent.CTRL_DOWN_MASK));
saveItem.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_S, InputEvent.CTRL_DOWN_MASK));

// 添加分割线
fileMenu.add(newItem);
fileMenu.add(openItem);
fileMenu.add(saveItem);
fileMenu.addSeparator();
fileMenu.add(exitItem);

// 事件监听
exitItem.addActionListener(e -> System.exit(0));

// 编辑菜单
JMenu editMenu = new JMenu("编辑");
editMenu.add(new JMenuItem("剪切"));
editMenu.add(new JMenuItem("复制"));
editMenu.add(new JMenuItem("粘贴"));

// 添加到菜单栏
menuBar.add(fileMenu);
menuBar.add(editMenu);

// 设置到框架
frame.setJMenuBar(menuBar);

8.3 弹出菜单(JPopupMenu)

java

JPopupMenu popupMenu = new JPopupMenu();
popupMenu.add(new JMenuItem("复制"));
popupMenu.add(new JMenuItem("粘贴"));
popupMenu.addSeparator();
popupMenu.add(new JMenuItem("删除"));

// 鼠标右键弹出
panel.addMouseListener(new MouseAdapter() {
    @Override
    public void mouseReleased(MouseEvent e) {
        if (e.isPopupTrigger()) {
            popupMenu.show(panel, e.getX(), e.getY());
        }
    }
});

8.4 选项卡(JTabbedPane)

java

JTabbedPane tabbedPane = new JTabbedPane();

JPanel tab1 = new JPanel();
tab1.add(new JLabel("这是第一个选项卡"));
tab1.setBackground(Color.WHITE);

JPanel tab2 = new JPanel();
tab2.add(new JButton("按钮"));
tab2.setBackground(Color.LIGHT_GRAY);

JPanel tab3 = new JPanel(new GridLayout(2, 2));
tab3.add(new JCheckBox("选项1"));
tab3.add(new JCheckBox("选项2"));

tabbedPane.addTab("基本信息", tab1);
tabbedPane.addTab("操作", tab2);
tabbedPane.addTab("设置", tab3);

frame.add(tabbedPane, BorderLayout.CENTER);

8.5 滚动面板(JScrollPane)

java

// 文本域需要滚动
JTextArea textArea = new JTextArea(10, 30);
JScrollPane scrollPane = new JScrollPane(textArea);
// 设置滚动策略
scrollPane.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED);
scrollPane.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED);

// 表格需要滚动
JTable table = new JTable(data, columns);
JScrollPane tableScroll = new JScrollPane(table);

8.6 外观(Look and Feel)

java

// 系统外观(Windows/Mac/Linux)
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());

// 跨平台外观(Metal)
UIManager.setLookAndFeel(UIManager.getCrossPlatformLookAndFeelClassName());

// Nimbus 外观(JDK 6+)
UIManager.setLookAndFeel("javax.swing.plaf.nimbus.NimbusLookAndFeel");

// 所有外观
UIManager.LookAndFeelInfo[] looks = UIManager.getInstalledLookAndFeels();
for (UIManager.LookAndFeelInfo info : looks) {
    System.out.println(info.getName() + ": " + info.getClassName());
}

8.7 线程安全(Swing 与多线程)

java

// Swing 组件必须在事件调度线程(EDT)中创建和更新
// 错误方式
new Thread(() -> {
    label.setText("新文本");  // ❌ 多线程操作 Swing 不安全
}).start();

// 正确方式1:invokeLater
SwingUtilities.invokeLater(() -> {
    label.setText("新文本");
});

// 正确方式2:invokeAndWait(等待执行完成)
try {
    SwingUtilities.invokeAndWait(() -> {
        label.setText("新文本");
    });
} catch (Exception e) {
    e.printStackTrace();
}

// 在主方法中创建 GUI
public static void main(String[] args) {
    SwingUtilities.invokeLater(() -> new LoginDemo());
}

第九部分:JavaFX 简要介绍

9.1 特点

  • 现代化 GUI 框架,替代 Swing

  • 支持 CSS 样式

  • 支持 FXML(声明式 UI)

  • 硬件加速渲染

  • 内置动画、图表、WebView

9.2 简单示例

java

import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.layout.StackPane;
import javafx.stage.Stage;

public class JavaFXDemo extends Application {
    @Override
    public void start(Stage primaryStage) {
        Button btn = new Button("点击我");
        btn.setOnAction(e -> System.out.println("按钮被点击"));
        
        StackPane root = new StackPane();
        root.getChildren().add(btn);
        
        Scene scene = new Scene(root, 300, 200);
        primaryStage.setTitle("JavaFX 示例");
        primaryStage.setScene(scene);
        primaryStage.show();
    }
    
    public static void main(String[] args) {
        launch(args);
    }
}

第十部分:Swing 最佳实践与常见问题

10.1 最佳实践

  1. 所有 GUI 操作在 EDT 线程中执行:使用 SwingUtilities.invokeLater()

  2. 避免使用绝对布局:使用布局管理器

  3. 组件创建与显示分离:先创建所有组件,最后 setVisible(true)

  4. 使用 pack() 而不是 setSize():让窗口自适应

  5. 大文本用 JTextArea + JScrollPane

  6. 密码用 JPasswordField,用 char[] 获取

  7. 长时间操作使用 SwingWorker(后台任务)

10.2 常见问题

问题 原因 解决方案
组件不显示 未添加到容器或未设置布局 检查 add() 和布局管理器
窗口无法关闭 未设置关闭操作 setDefaultCloseOperation()
界面闪烁 未使用双缓冲 Swing 默认双缓冲,检查是否关闭
卡顿/无响应 在 EDT 执行耗时操作 使用 SwingWorker 或新线程
乱码 编码问题 设置 -Dfile.encoding=UTF-8

10.3 SwingWorker(后台任务)

java

import javax.swing.*;

public class SwingWorkerDemo {
    public static void main(String[] args) {
        JFrame frame = new JFrame("进度示例");
        frame.setSize(300, 150);
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.setLayout(new FlowLayout());
        
        JProgressBar progressBar = new JProgressBar(0, 100);
        progressBar.setStringPainted(true);
        JButton startBtn = new JButton("开始");
        JLabel label = new JLabel("准备就绪");
        
        startBtn.addActionListener(e -> {
            startBtn.setEnabled(false);
            
            // SwingWorker 执行后台任务
            SwingWorker<Void, Integer> worker = new SwingWorker<Void, Integer>() {
                @Override
                protected Void doInBackground() throws Exception {
                    for (int i = 0; i <= 100; i++) {
                        Thread.sleep(50);  // 模拟耗时
                        publish(i);        // 更新进度
                    }
                    return null;
                }
                
                @Override
                protected void process(Integer... chunks) {
                    int progress = chunks[chunks.length - 1];
                    progressBar.setValue(progress);
                    label.setText("进度:" + progress + "%");
                }
                
                @Override
                protected void done() {
                    startBtn.setEnabled(true);
                    label.setText("完成!");
                }
            };
            
            worker.execute();
        });
        
        frame.add(progressBar);
        frame.add(startBtn);
        frame.add(label);
        frame.setVisible(true);
    }
}

第十一部分:知识点速查表

分类 组件/类 用途
顶层容器 JFrame 主窗口
中间容器 JPanel 组织组件
基本组件 JLabel 显示文本/图标
JButton 按钮
JTextField 单行文本输入
JTextArea 多行文本输入
JPasswordField 密码输入
选择组件 JCheckBox 复选框
JRadioButton 单选按钮
JComboBox 下拉列表
JList 列表
高级组件 JTable 表格
JTree 树形结构
JTabbedPane 选项卡
JScrollPane 滚动面板
布局 FlowLayout 流式布局
BorderLayout 边界布局
GridLayout 网格布局
CardLayout 卡片布局
BoxLayout 箱式布局
事件 ActionListener 动作事件
MouseListener 鼠标事件
KeyListener 键盘事件
ItemListener 选项事件
对话框 JOptionPane 标准对话框
菜单 JMenuBar 菜单栏
JPopupMenu 右键菜单
线程 SwingUtilities EDT 工具类
SwingWorker 后台任务

第十二部分:记忆口诀

text

Swing 组件分三层:顶层容器 JFrame,中间面板 JPanel
基本组件有标签,按钮文本框和密码
选择组件复选框,单选按钮下拉框
表格列表滚动条,选项卡和菜单栏

布局管理器五兄弟:流式边界和网格,卡片箱式最灵活
事件模型三要素:事件源和监听器,事件对象传信息
匿名内部类最常用,Lambda 表达式更简洁

GUI 操作在 EDT,耗时任务用 Worker
创建完毕再显示,setVisible 最后放
Logo

openEuler 是由开放原子开源基金会孵化的全场景开源操作系统项目,面向数字基础设施四大核心场景(服务器、云计算、边缘计算、嵌入式),全面支持 ARM、x86、RISC-V、loongArch、PowerPC、SW-64 等多样性计算架构

更多推荐