选用JavaFX而非Swing因后者样式陈旧且UI线程交互易抛IllegalStateException;JavaFX提供Timeline、Alert及绑定控件,更适配提醒闭环,需用Platform.runLater更新UI、ScheduledExecutorService管理定时任务、ObservableList实现自动刷新。

为什么不用 Swing 而选 JavaFX 来做提醒工具界面
Swing 组件默认样式陈旧,Timer 和线程调度与 UI 交互容易出 IllegalStateException: Not on FX application thread 错误;JavaFX 提供原生的 Timeline、Alert 和绑定式控件(如 ObservableList),更适合实现「添加事项→设置时间→到点弹窗提醒」这个闭环。初学者若硬套 Swing,大概率会在 SwingUtilities.invokeLater 和 Timer 嵌套里迷失。
实操建议:
- 用
javafx.application.Application启动,别写main里直接 new Frame - 所有 UI 更新(比如在列表加一条新事项)必须走
Platform.runLater() - 提醒触发逻辑不要放在
Button.setOnAction里直接 sleep —— 那会卡死整个界面
如何让提醒准时触发且不重复弹窗
核心是用 ScheduledExecutorService 管理定时任务,而不是轮询或 Thread.sleep。每个提醒事项对应一个 ScheduledFuture,保存在 Map 中,方便取消或替换。
常见错误现象:同一事项点了两次“添加”,结果到点弹两个一模一样的 Alert。
立即学习“Java免费学习笔记(深入)”;
实操建议:
- 给每条事项生成唯一 ID(例如
UUID.randomUUID().toString()),作为 map 的 key - 添加前先检查该 ID 是否已存在,存在则调用
future.cancel(true)再新建 - 触发时用
Alert alert = new Alert(AlertType.INFORMATION),设alert.setHeaderText(null)避免默认标题干扰 - 务必调用
alert.showAndWait()而非show(),否则用户还没点确认,后续提醒可能叠加
数据怎么存?别急着上 SQLite 或 JSON 文件
初学者项目目标是跑通流程,不是造轮子。内存存储 + 序列化到本地文件(如 reminders.ser)足够用,且避免引入 JDBC 或 Jackson 依赖带来的配置和异常处理负担。
Lucene是apache软件基金会4 jakarta项目组的一个子项目,是一个开放源代码的全文检索引擎工具包,但它不是一个完整的全文检索引擎,而是一个全文检索引擎的架构,提供了完整的查询引擎和索引引擎,部分文本分析引擎(英文与德文两种西方语言)。 Lucene的目的是为软件开发人员提供一个简单易用的工具包,以方便的在目标系统中实现全文检索的功能,或者是以此为基础建立起完整的全文检索引擎。Lucene提供了一个简单却强大的应用程式接口,能够做全文索引和搜寻。在Java开发环境里Lucene是一个成熟的免
使用场景:关闭程序再打开,上次添加的事项还在列表里。
实操建议:
- 定义类时加上
implements Serializable,字段用private static final long serialVersionUID = 1L - 读写用
ObjectOutputStream/ObjectInputStream,路径固定为"./reminders.ser" - 捕获
IOException和ClassNotFoundException,失败时清空列表并 log,别让程序崩溃 - 不要在 JavaFX 主线程里做文件 IO —— 用
Executors.newCachedThreadPool()异步读写
为什么点击“删除”后列表没刷新
因为直接操作了 ArrayList,但 JavaFX 的 ListView 绑定的是 ObservableList。改底层数据源却不通知 UI,等于白删。
典型错误代码:
items.remove(selectedItem); // items 是 ArrayList
实操建议:
- 初始化时用
FXCollections.observableArrayList()创建列表 - 绑定到 ListView:
listView.setItems(observableItems);
- 删除时调用
observableItems.remove(selectedItem),自动触发 UI 更新 - 如果要支持多选删除,用
listView.getSelectionModel().getSelectedItems()获取 ObservableList 视图,再批量 remove









