slug
type
status
category
date
summary
tags
password
icon
托福词汇记忆程序
我直接从上到下、按块+按行帮你拆开讲一遍,顺便解释一下 Swing 这套机制在干嘛。
import javax.swing.*;
引入 Swing 图形界面库里所有类(
JFrame, JLabel 等)供本文件使用。import java.awt.*;
引入 AWT 里的基础图形类,比如
Font、Color、Component 等。import java.awt.event.KeyAdapter;
引入
KeyAdapter,这是一个“键盘事件适配器”,帮你简化键盘监听代码(不用实现接口里的所有方法,只重写自己关心的那个)。import java.awt.event.KeyEvent;
引入
KeyEvent 类,用来表示一个键盘事件(按下了哪个键、键码是多少等)。import java.io.File;
用于表示文件对象,比如
new File("toefl.txt")。import java.nio.charset.StandardCharsets;
提供标准字符集常量,比如
StandardCharsets.UTF_8,保证按 UTF-8 编码读取文件。import java.util.ArrayList;/Collections;/List;/Random;/Scanner;ArrayList,List:动态数组和接口,用来存储一条条WordEntry。Collections:工具类,里面有shuffle等方法。Random:随机数生成器。Scanner:方便地按行读取文件内容。
定义一个公共类
ToeflFlashcardsGUI,类名和文件名要一致。整个程序(包含
main)都写在这个类里。static class WordEntry { ... }
在主类里面定义了一个“静态内部类”
WordEntry,用来表示“一条单词记录”。String word;
保存英文单词。
String chinese;
保存中文解释。
WordEntry(String w, String c) { ... }
构造器,创建
WordEntry 对象时把英文和中文传进来。word = w;/chinese = c;
把构造器参数赋值给对象的成员变量。
以后我们可以写:
new WordEntry("abandon", "放弃")。private static List<WordEntry> words = new ArrayList<>();- 定义一个静态
List,存所有单词卡片。 static是因为main也是静态的,方便在静态方法里直接使用。
private static int index = 0;
当前正在看的卡片下标(从 0 开始)。
private static boolean showingChinese = false;- 标记“当前画面是不是在显示中文解释”。
false:只显示英文;true:显示中英文。
private static JLabel label;
界面上那个大字显示区域,用
JLabel 来显示英文或带 HTML 的中英文。入口方法
main:程序从这里开始执行。throws Exception 是偷懒写法:如果文件读取出错等异常,直接往外抛,不在这里 try-catch。File file = new File("toefl.txt");
创建一个
File 对象,指向工程根目录下的 toefl.txt 文件。Scanner scan = new Scanner(file, StandardCharsets.UTF_8);
创建一个扫描器,用 UTF-8 编码从这个文件里读数据(防止中文乱码)。
while (scan.hasNextLine()) { ... }
只要文件中还有下一行,就一直循环。
String line = scan.nextLine().trim();
读取一整行字符串,并用
trim() 去掉首尾空白(空格、制表符、回车等)。if (line.isEmpty()) continue;
如果这一行是空的(比如空白行),就跳过,进入下一轮循环。
- 注释:
// 期望:英文#中文
说明每一行的格式应该是:
abandon#放弃;抛弃String[] parts = line.split("#", 2);- 用
#分隔这一行。 - 第二个参数
2表示最多分成 2 段: parts[0]:#前面的英文parts[1]:#后面的“剩下的所有内容”(中文解释,可以包含#再往后的东西)。- 这样可以避免一行里有多个
#时被切得太碎。
if (parts.length == 2) { ... }
只有确实分成了 2 段,才认为这行合法。
words.add(new WordEntry(parts[0].trim(), parts[1].trim()));- 创建一个
WordEntry对象: - 英文:
parts[0].trim() - 中文:
parts[1].trim() - 然后放进
words这个列表里。
用完
Scanner 要关闭,释放文件资源/文件句柄。- 如果
words为空,说明文件里没读到任何合法的“英文#中文”数据: - 在控制台输出提示。
return;直接结束main方法,也就是程序结束,不用再打开界面。
- 注释说:随机打乱顺序。
Collections.shuffle(words, new Random());
使用给定的
Random 随机数生成器把 words 列表顺序打乱,相当于打乱卡片顺序,每次运行程序,出牌顺序都不一样。
JFrame frame = new JFrame("...")
创建一个主窗口,标题栏显示
"TOEFL 闪卡 — 按空格翻牌(无需回车)"。frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
当点击窗口右上角的关闭按钮时,整个程序退出(而不只是隐藏窗口)。
frame.setSize(900, 600);
设置窗口大小为宽 900 像素,高 600 像素。
把窗口放在屏幕中央显示:
setLocationRelativeTo(null) 是个小技巧,参数 null 表示相对整个屏幕居中。label = new JLabel("", SwingConstants.CENTER);- 创建一个
JLabel,初始文字是空字符串。 - 第二个参数指定对齐方式为“水平居中”。
label.setFont(new Font("SansSerif", Font.BOLD, 60));
设置字体为无衬线字体(类似黑体),加粗,字号 60,很大一行字。
frame.add(label);
把这个标签添加到窗口的内容区域中(默认布局 BorderLayout,会放在中间)。
刚开始先显示第一张卡片的英文(下方我们定义的
showEnglish() 方法)。frame.addKeyListener(new KeyAdapter() { ... })
给
frame 注册一个键盘监听器对象。这个对象继承自
KeyAdapter,我们只重写 keyPressed 这个方法。public void keyPressed(KeyEvent e)
当某个键被按下时,这个方法就会被自动调用。
参数
e 里包含了“按下的是哪个键”等信息。⚠️ 实际使用中,常见的细节是:要确保 frame 聚焦(或者你更常对 frame.getContentPane() 或者某个 JComponent 添加 Key Listener,并设为可聚焦)。不过在这个简单例子里一般够用。
if (e.getKeyCode() == KeyEvent.VK_SPACE) { ... }
判断这次按下的键是不是空格键(
VK_SPACE)。- 注释:
// 空格 → 翻牌或下一张 - 如果目前只显示英文 → 改为显示中文(翻牌)。
- 如果已经显示了中文 → 切换到下一张卡片。
表示空格键的逻辑:
if (!showingChinese) { showChinese(); } else { nextCard(); }- 当
showingChinese == false:只显示英文,这时按空格调用showChinese()。 - 当
showingChinese == true:已经显示了中英文,再按空格就nextCard()换下一张卡。
if (e.getKeyCode() == KeyEvent.VK_Q)
检查是否按下了键盘上的字母
Q 键。System.exit(0);
直接结束 JVM 进程,程序立即退出(0 表示正常退出)。
}结束keyPressed方法;}结束匿名内部类new KeyAdapter(){...};
); 把这个监听器添加到 frame。frame.setVisible(true);
把窗口显示在屏幕上。
如果不调用这句,窗口虽然已经创建好了,但不会出现。
}结束main方法。
三个辅助方法:showEnglish、showChinese、nextCard
- 注释说明:这个方法的功能是“显示当前卡片的英文”。
WordEntry w = words.get(index);
从
words 列表里取出当前下标 index 对应的那一条单词。label.setText(w.word);
把
JLabel 的文字设为这个单词的英文。此时标签只显示英文。
showingChinese = false;
标记现在是“只显示英文”的状态。
- 注释:“显示中文”。
WordEntry w = words.get(index);
同样先取出当前卡片。
label.setText("<html><center>" + w.word + "<br><br>" + w.chinese + "</center></html>");- 以
<html> ... </html>包起来,JLabel 会按 HTML 渲染。 <center> ... </center>:在标签内部水平居中(其实JLabel自己也有居中属性,这里是双保险)。w.word:先显示英文单词。<br><br>:两个<br>换行,相当于中间空一行。w.chinese:下面显示中文解释。
这里使用了
JLabel 支持的简单 HTML:效果:
英文单词(空一行)中文解释
showingChinese = true;
标记状态为“当前显示了中文”。
- 注释:“下一张”。
index++;
当前索引加 1,指向下一条单词。
if (index >= words.size()) { ... }Collections.shuffle(words);index = 0;
如果
index 超出了列表的最后一个位置,说明当前已经是最后一张了:再次把
words 的顺序随机打乱,相当于“重新洗牌”,进入下一轮复习。把索引重置到 0,从洗牌后的第一张重新开始。
showEnglish();
无论是否洗牌,进入新的一张卡片后,第一步都是只显示英文,让你先回忆含义,再按空格看中文。
- 最后一个
}结束整个类ToeflFlashcardsGUI。
雅思词汇记忆程序
打包方法:
当然可以!下面给你一份完整、结构清晰、从 IntelliJ 到最终可运行 EXE 的 全流程总结。这份总结以后你做任何 Java 桌面程序都可以直接复用。
✅ 《从 IntelliJ 到 Windows EXE 的完整打包流程总结》
1. 环境准备
✔ 安装 JDK(例如 JDK 23)
确认命令行可运行:
如果
jpackage 报“不是内部或外部命令”,配置环境变量:- 新建
JAVA_HOME→ 指向 JDK
- 编辑 Path → 加入
%JAVA_HOME%\bin
2. 在 IntelliJ 生成可运行 JAR
✔ IntelliJ → Build Artifact
- File → Project Structure → Artifacts
- 添加:
- JAR → From modules with dependencies
- 主类选
ToeflFlashcardsGUI
- Build → Build Artifacts → Build
得到例如:
3. 准备 jpackage 的 input 目录
创建打包用目录(例):
关键规则:input 下的内容会被完整复制到最终 app\ 目录
4. jpackage 打包(生成 app-image)
运行命令:
生成:
不要只拷 exe,exe 必须与 runtime 一起存在,否则无法启动 JVM。
5. 解决找不到 toefl.txt 的问题
打包后,工作目录 = ToeflFlashcards(exe 所在目录)
但
toefl.txt 在:Java 默认
new File("toefl.txt") 找不到 → 程序闪退 → “Failed to launch JVM”✔ 解决方案:代码中支持两种路径
这样:
- IntelliJ 里运行:读当前目录
- 打包后运行:读 app\toefl.txt
- 都正常
6. 添加 catch,显示错误,不闪退
用对话框捕获异常:
任何错误都会弹窗显示,便于调试。
7. 最终 EXE 使用方式(给学生/同学)
把整个:
打包成 zip 发送。
解压后:
完全不需要安装 Java,不需要环境变量。
🎉 最终效果
你已经成功做到:
- ✔ IntelliJ 程序 → 可运行 JAR
- ✔ JDK 自带 jpackage → 打包成 Windows 原生 EXE
- ✔ 自动附带精简版 runtime(无需对方安装 Java)
- ✔ 正确处理资源文件(toefl.txt)
- ✔ 程序不会闪退,有异常提示窗口
- ✔ 最终能分发给学生/朋友直接用
这是 Java 桌面发布的完整专业流程,以后你做更多 GUI 小工具(单词本/工具箱/测试程序)都可以复用这一整套体系。
如果你愿意,我还能帮你生成:
- 打包脚本 bat(双击自动打包)
- 自动更新资源文件的方案
- 打包成真正的安装程序(MSI/EXE Installer)
只需要告诉我你想要哪一种即可!
- 作者:现代数学启蒙
- 链接:https://www.math1234567.com/article/vocabularymemory
- 声明:本文采用 CC BY-NC-SA 4.0 许可协议,转载请注明出处。
相关文章










