ARTICLE DETAIL

资讯详情

深耕商务建站与企业官网运营的一线实战洞察。

Java Swing会员管理系统:CSV/Excel读写与桌面GUI实战解析

Java Swing会员管理系统:CSV/Excel读写与桌面GUI实战解析 简介基于Java Swing的会员管理系统窗体GUI项目面向正在学习Java桌面应用开发、需要掌握GUI编程与数据文件读写结合的初学者。项目以Swing组件构建会员信息管理界面涵盖添加、查看、修改会员状态的窗体交互逻辑完整演示了通过OpenCSV解析CSV文件、借助Apache POI读取Excel表格数据的典型流程最终将会员数据加载至JTable表格中展示。压缩包共21个文件核心为11个Java源文件辅以6个XML配置、1个CSV示例数据文件并含IntelliJ IDEA项目配置与目录结构整体体积仅26KB轻量易用。已有215人学习下载适合作为课程设计、毕业设计参考或Java GUI入门练习项目。通过项目可直观理解Swing组件布局、事件监听机制以及CSV/Excel数据文件与界面交互的完整实现思路动手改写即可扩展为更完整的会员业务管理系统。1. 先看清定位它解决的不是“写窗体”是“Excel 名单怎么管”当手里那份几千行的会员 Excel 名单需要每天维护新增会员、改手机号、统计积分还得定期导出一份交给财务时手工复制累到怀疑人生。这个 java swing 会员管理系统窗体 GUI 项目解决的就是这件事用桌面窗体完成会员增删改查、积分余额维护和 CSV/Excel 文件读取登录就能用。它适合两类人想学 Swing 窗体开发的初学者以及确实需要一个轻量会员台账工具的商家或工作室。我拆这份源码最大的感受是它足够朴素——Swing 自带组件、JDK 内置文件接口、一个 data 目录存 CSV却把身份验证、表格刷新、文件编码处理这些真实项目里的关键细节完整摊开了。2. 技术选型拆解为什么这个时代还有人用 Swing 和 CSV2.1 业务定位这个窗体到底管什么先给项目画个像。功能上它是一个典型的单机版会员管理工具管理员通过登录窗体进入主界面在主窗体的 JTable 里看到会员列表支持新增会员、编辑姓名和手机号、充值余额、调整积分、删除会员所有数据落在一个 members.csv 文件里。列表上方有按钮把数据导入、导出 CSV方便和 Excel 对接。没有数据库、没有后端服务整个程序就是“一个窗体 一个 CSV 文件”。业务上它还覆盖了两个让新手头疼的点第一手机号、积分、金额这些字段的格式校验——写在事件监听器里还是抽成独立方法第二CSV 文件中文乱码——很多人第一次做导出功能用 Excel 打开全是问号。这两点在源码里都有对应处理也正是标题里 csv excel 读取被单独强调的原因。我拆完这个项目后觉得它本质上不是个“写界面”的项目而是个“文件读写 数据模型 事件驱动”的复合练习。只要把这套链路跑通Swing 桌面应用的基本盘就吃透了。2.2 从“能用”到“好改”的包结构很多 Swing 课设会把所有代码塞进一个 MainFrame.java 里这个项目没有。打开源码能看到按职责拆开的包member-system/ ├── src/com/demo/member/ │ ├── MainApp.java // 启动入口 │ ├── config/ │ │ └── config.properties // 登录账号配置 │ ├── model/ │ │ ├── Member.java // 会员实体 │ │ └── MemberTableModel.java // JTable 数据模型 │ ├── view/ │ │ ├── LoginFrame.java // 登录窗体 │ │ ├── MainFrame.java // 主窗体 │ │ └── MemberEditDialog.java // 新增/编辑对话框 │ └── util/ │ ├── CsvUtil.java // CSV 读写工具 │ └── FileUtil.java // 文件路径和目录处理 └── data/ └── members.csv // 运行后自动生成的会员数据这个结构说不上多高级但已经把“数据模型”“界面”“工具类”分开了。我拆这个项目时最满意的点是 MemberTableModel 单独成类而不是在 MainFrame 里重写 AbstractTableModel——这样做的好处是后续如果要加搜索、排序只要扩展这个 model不用动窗体代码。建议照这个顺序读源码MainApp → LoginFrame → MainFrame → MemberTableModel → CsvUtil。前面 4 个文件对应“启动、登录、主界面、数据模型”这条主线捋完它们再看 CsvUtil 的读写细节就不会被一堆 IO 方法绕晕。2.3 技术栈对比Swing 和 CSV 是“过时”还是“合适”用一张表说清为什么这组选型在这个场景下能打方案优点劣势适合场景Swing CSVJDK 内置零依赖、启动快、代码直观界面不如 Web 华丽、并发能力弱单机会员台账、教学演示JavaFX SQLite界面现代、支持 CSS需要额外库、学习成本略高要做商业级桌面的小工具Web 管理后台可多人访问、界面灵活要部署服务器、前后端开发量变大多人使用的线上系统一个门店或工作室的会员管理用户量通常就是几个人几千行 CSV 完全扛得住。Swing 组件全是 JDK 自带的不存在版本冲突拿到源码直接编译就能跑。CSV 的好处是用户肉眼能看懂导出后发给老板老板用 Excel 就能打开不需要解释什么叫数据库。凡是“数据要给人看”的轻量场景CSV 反而比数据库更实在。当然这个项目如果要做成多门店连锁CSV 肯定不够用磁盘文件并发读写会出问题。所以第 6 章我会讲怎么把它换成数据库先把当前最核心的模块链路看完。3. 核心模块拆解从一次登录到一次会员导出的完整链路这一章按“启动 → 登录 → 列表展示 → 增删改查 → CSV 导出”的顺序把项目主链路走一遍。理解这条链路后你想改任何功能都知道该去哪个类里动刀。3.1 启动入口与登录校验先过身份再干活MainApp 是整个程序的入口代码很少但有两个细节值得看public class MainApp { public static void main(String[] args) { // 用 invokeLater 把界面创建丢到事件队列 SwingUtilities.invokeLater(() - { try { UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName()); } catch (Exception ignored) { // 系统外观设置失败就用默认外观 } new LoginFrame().setVisible(true); }); } }第一次写 Swing 的人可能不理解为啥要套一个 SwingUtilities.invokeLater。简单说Swing 所有界面操作必须发生在 EDTEvent Dispatch Thread上否则会出现界面画到一半就卡住、按钮不响应这种“看着没报错但不正常”的现象。main 线程跑完就销毁了把任务丢进事件队列后 EDT 才能接管。setLookAndFeel 是让界面跟随当前操作系统风格Windows 上更像本地程序避免默认的 Metal 风格显得很“远古”。登录窗体的校验实现也值得看它暴露了一个桌面项目常见的设计取舍// 登录按钮的监听器只做流程编排不做具体业务 loginBtn.addActionListener(e - { String user usernameField.getText().trim(); String pwd new String(passwordField.getPassword()); if (checkLogin(user, pwd)) { dispose(); // 关闭登录窗口 new MainFrame().setVisible(true); } else { JOptionPane.showMessageDialog( LoginFrame.this, 账号或密码错误, 登录失败, JOptionPane.ERROR_MESSAGE); } });这里有三个要点。第一密码框必须用 JPasswordField 的 getPassword() 拿 char[] 而不是 getText()因为 char[] 用完后可以手动清空String 会留在内存里。第二登录成功后调用 dispose() 而不是 setVisible(false)因为登录窗体已经完成使命留在事件队列里只会占资源。第三checkLogin 实际是去 config.properties 读预设的管理员账号演示项目这么干可以生产环境至少要做加盐哈希。3.2 会员列表与 JTable 模型别直接往 JTable 塞数据主窗体最核心的控件是 JTable表格里的数据来自 MemberTableModel。这个类继承 AbstractTableModel把 Member 对象列表翻译成单元格public class MemberTableModel extends AbstractTableModel { private final String[] columnNames {编号, 姓名, 手机号, 会员等级, 积分, 余额(元)}; private final ListMember members new ArrayList(); public void setMembers(ListMember list) { members.clear(); members.addAll(list); // 关键数据变化后必须通知表格重绘 fireTableDataChanged(); } public void addMember(Member m) { members.add(m); // 只通知“新增了一行”比全表刷新更高效 fireTableRowsInserted(members.size() - 1, members.size() - 1); } Override public int getRowCount() { return members.size(); } Override public int getColumnCount() { return columnNames.length; } Override public Object getValueAt(int rowIndex, int columnIndex) { Member m members.get(rowIndex); return switch (columnIndex) { case 0 - m.getId(); case 1 - m.getName(); case 2 - m.getPhone(); case 3 - m.getLevel(); case 4 - m.getPoints(); case 5 - m.getBalance(); default - ; }; } }这段代码是整张表格能正常显示的关键。很多人第一次做 JTable 会直接拿 JTable 构造函数塞一个二维数组那样做的问题是数据变了表格不知道。正确的做法是让数据模型成为唯一数据源增删改都要通过 model 的方法改完调用 fireTableDataChanged 或 fireTableRowsInserted 通知视图重绘。注意一个兼容性细节这里用了 switch 表达式它在 Java 14 才开始正式支持。如果你本地装的是 JDK 8需要改回传统的 switch 分支写法否则编译直接报错。这个坑在很多教程里不会主动提我在第 5 章会专门说。3.3 增删改查与表单校验把校验从业务里抽出来新增会员和编辑会员共用一个 MemberEditDialog内部几个 JTextField 分别接收姓名、手机号、积分、余额。保存按钮的监听器最忌讳的写法是把一大堆 if 判断直接堆在监听器里既难看又难测。常规做法是先抽一个校验方法private boolean validateForm() { String name nameField.getText().trim(); String phone phoneField.getText().trim(); String pointsText pointsField.getText().trim(); if (name.isEmpty()) { JOptionPane.showMessageDialog(this, 姓名不能为空); return false; } if (!phone.matches(1\\d{10})) { JOptionPane.showMessageDialog(this, 手机号格式不正确); return false; } if (!pointsText.matches(\\d)) { JOptionPane.showMessageDialog(this, 积分必须是正整数); return false; } return true; }保存按钮的监听器里就只剩三行先 validateForm()再构造 Member最后调 model.addMember(m)。这套写法的好处是以后想加“余额不能为负数”“姓名最大长度”这类规则只需要在 validateForm 里加判断不污染界面代码。手机号正则1\\d{10}是大陆手机号的常用简化校验只验证 11 位且以 1 开头。如果项目要支持座机正则要放宽或拆成多个规则。积分这里要求只能输入数字解析成 int 用 Integer.parseInt(pointsText)如果用户输入 12.5 会在 validateForm 就被拦下不会走到数据层。3.4 CSV 导入导出Excel 能不能直接打开就看这两段代码标题里专门写了 CSV excel 文件读取说明开发者在文件读写上确实用了心。导出功能的实现最能体现“Excel 友好”四个字public static void exportToCsv(ListMember members, File file) throws IOException { // 用 OutputStreamWriter 指定 UTF-8并手动写入 BOM try (Writer out new BufferedWriter(new OutputStreamWriter( new FileOutputStream(file), StandardCharsets.UTF_8))) { out.write(\uFEFF); // BOMExcel 识别 UTF-8 的关键 out.write(编号,姓名,手机号,会员等级,积分,余额); out.write(\r\n); for (Member m : members) { String line String.format(%d,%s,%s,%s,%d,%.2f%n, m.getId(), m.getName(), m.getPhone(), m.getLevel(), m.getPoints(), m.getBalance()); out.write(line); } } }\uFEFF这三行字符是导出功能最大的功臣。UTF-8 编码的文件直接在 Windows 的 Excel 里双击打开时Excel 默认按 ANSI 解析中文就会乱码写入 BOM 后 Excel 才会判定这是 UTF-8 文件。这就是很多开发者导出 CSV 后在 Excel 里看到乱码但在记事本里又正常的原因。导入功能是对称的读文件逻辑public static ListMember importFromCsv(File file) throws IOException { ListMember result new ArrayList(); // 常见做法先按 UTF-8 读读出来乱码再退回 GBK try (BufferedReader reader new BufferedReader(new InputStreamReader( new FileInputStream(file), StandardCharsets.UTF_8))) { String line; int lineNo 0; while ((line reader.readLine()) ! null) { lineNo; line line.replace(\uFEFF, ); // 去掉首行可能带有的 BOM if (line.isBlank()) continue; if (lineNo 1 line.startsWith(编号)) continue; // 跳过表头 String[] cols line.split(,, -1); if (cols.length 6) continue; // 脏数据直接跳过 result.add(new Member( 0, cols[1].trim(), cols[2].trim(), cols[3].trim(), Integer.parseInt(cols[4].trim()), Double.parseDouble(cols[5].trim()) )); } } return result; }导入时最容易忽略的是 BOM 和脏数据。从 Excel 手工另存的 CSV第一行第一列可能带着\uFEFF直接 split 后 cols[0] 是\uFEFF1parseInt 就会炸。这里用replace(\uFEFF, )先清洗。split(,, -1)里的 -1 表示保留末尾空字符串避免某行数据末尾少了一个字段时被静默截断。另外cols.length 6的防御性判断保证坏行不会让整个导入崩溃。4. 把项目跑起来从导入 IDE 到处理第一份会员数据源码拿到手第一件事不是逐行读代码而是先让它跑起来。这章按“打开 → 配置 → 运行 → 验证”的顺序走一遍每一步都有注意点。4.1 核对目录结构和数据文件位置用 IDEA 或 Eclipse 打开项目根目录后先确认三件事源码目录是不是 src有没有 data 目录项目 SDK 是否已经配置。如果缺 data 目录不用手动创建FileUtil 会在启动时自动创建。但你一定要知道数据文件默认放在项目根目录的 data/members.csv 下而不是写死在 C 盘某个路径。FileUtil 里常见做法是用相对路径public static File getMembersFile() throws IOException { // 常见做法相对项目根目录避免写死 C 盘绝对路径 Path dataPath Paths.get(data).toAbsolutePath(); Files.createDirectories(dataPath); return dataPath.resolve(members.csv).toFile(); }这个设计的目的是让项目换电脑后依然能跑。如果源码里写死C:/member/data/members.csv换一台没有这个目录的机器就得报错用相对路径 toAbsolutePath无论项目放在哪个盘都会在项目根目录下找 data 文件夹。4.2 IDEA 里配置运行入口和三处编码设置运行入口是 MainApp右键直接 Run 就行。不过运行前最好把三处编码统一成 UTF-8Settings 里 Editor File Encodings 的 Global Encoding、Project Encoding 都设为 UTF-8properties 文件的编码也设成 UTF-8。否则 config.properties 里的中文提示可能变成乱码。如果不用 IDE用命令行编译也很快# Windows 或 Linux 下均可先编译所有 java 文件 javac -encoding UTF-8 -d out $(find src -name *.java) # 再运行主类 java -cp out com.demo.member.MainApp-encoding UTF-8必须有。源码里的中文字符串字面量、注释如果没指定编码javac 会按系统默认编码读取一旦和文件实际编码不一致轻则注释乱码重则编译直接失败。4.3 验证登录和会员增删改查的完整链路程序启动后会先弹出登录窗体用 config.properties 里配置的账号密码登录。进入主窗体后按这个顺序做一轮冒烟测试点击“新增会员”填写姓名、手机号、积分保存后观察 JTable 第一行是否立即出现该会员数据。选中该行点“编辑”把手机号改一位确认单元格内容变化。点“充值”余额字段增加对应金额且保留两位小数。点“导出 CSV”到 data 目录下找到导出的文件用 Excel 双击打开确认中文正常、各列没有挤在一起。关闭程序重新启动确认刚新增的会员还在——这条最关键说明数据已经写回 CSV不是只停留在内存里。第五步是很多人忽视的验证点。Swing 项目里数据“看起来保存了”和“真的落盘了”是两回事很多列表里显示正常重启后数据全没了就是因为只改了 List 没调 CsvUtil。4.4 看一眼 CSV 到底长什么样跑通后强烈建议用记事本打开 data 目录下的 CSV 文件你会看到类似这样的内容编号,姓名,手机号,会员等级,积分,余额 1,张伟,13812345678,普通会员,100,250.00 2,李娜,13912345678,黄金会员,300,680.50第一行是表头第二行开始是数据。你会发现编码是正确的 UTF-8带 BOM换行是 CRLF手机号没有被 Excel 变成科学计数法。这三条正是第 5 章要重点讲的坑——很多项目都栽在这几个看似不起眼的地方。5. 避坑指南Swing 老项目的五个经典翻车现场这一章从真实踩坑经历出发每个问题都按“现象 → 原因 → 解决”讲透。你照着排查一遍能少走一整个晚上的弯路。5.1 中文全是问号编译通过了界面显示乱码现象程序编译没报错但 JTable 里的中文显示成 ???弹出的提示框也是乱码。原因JVM 运行时默认按系统字符集解析文本。源码是 UTF-8 保存而 Windows 系统的默认字符集是 GBK代码里读文件时没有显式指定 Charset导致读错。解决所有读取文件的流都要显式指定编码例如new InputStreamReader(new FileInputStream(f), StandardCharsets.UTF_8)。另外把 IDEA 的编译编码、项目编码和运行参数 VM option-Dfile.encodingUTF-8三处统一基本就不会再翻车。5.2 JTable 数据不刷新新增会员后表格一动不动现象点保存后监听器里打印 List 的 size 确实增加了但界面上 JTable 没有任何反应。原因JTable 的数据模型不会自动感知 List 变化。如果直接操作 List 而没有调用 fireTableDataChanged 通知重绘视图层拿到的还是旧数据。解决永远通过 MemberTableModel 的 addMember、setMembers 方法去改数据这些方法内部会在变更后触发 fireTableRowsInserted 或 fireTableDataChanged。我自己的习惯是在 model 里禁止外部直接 get 回 List 再 add从源头杜绝这种问题。5.3 导入大 CSV 文件界面卡死点完按钮像死机了现象导入几千行数据时窗体变成“未响应”鼠标一直转圈要等十几秒才恢复。原因Swing 是单线程事件队列模型。文件读取、解析这些耗时操作如果直接放在监听器里会阻塞 EDT界面在那段时间内无法重绘看起来就像死机。解决把解析放到 SwingWorker 的后台线程里执行解析完再通过 done 方法把结果交给 EDT 更新表格。写导入功能时我会在解析前禁用导入按钮解析完再恢复避免用户重复点击导致二次卡死。5.4 导出的 CSV 在 Excel 里中文乱码现象用 IDEA 或 Windows 记事本打开导出文件中文正常用 Excel 双击打开中文全是乱码。原因Excel 识别 UTF-8 文件依赖文件开头存在 BOM字节序标记。很多写法用 FileWriter 直接输出纯文本文件没有 BOMExcel 就按 ANSI 解析中文自然不对。解决导出时先用out.write(\uFEFF)写入 BOM再写表头和数据也就是我在 3.4 节里的写法。这个坑特别隐蔽因为“编辑器打开正常”会让人误以为是 Excel 的问题。5.5 Excel 打开 CSV 所有数据挤在一列现象导出的 CSV 在 Excel 里打开一行数据全堆在 A1 单元格根本没有分列。原因CSV 虽然以逗号分隔但 Excel 对“是否分列”的判定依赖文件扩展名、首行内容和字段是否带引号。如果分隔符不规范或字段中有逗号却没加引号Excel 的智能分列就会失效。解决规范输出逗号分隔字段含逗号或换行时用双引号包裹UTF-8 带 BOM 导出每行以 \r\n 结尾。这三件事做到后Excel 双击打开就能自动分列。6. 二次开发怎样把它改成自己的会员系统跑通只是第一步。拿这个项目当课设底子时最想改的通常是两件事换数据库、加点自己的业务。这里给两个最顺手的改造方向。6.1 把 CSV 存储层替换成 JDBC现在 CsvUtil 承担了读写文件的全部职责换数据库的思路不是删掉它而是先抽象一个接口public interface MemberRepository { ListMember findAll(); void save(Member m); void update(Member m); void deleteById(int id); }CsvMemberRepository 实现现有逻辑再新建一个 JdbcMemberRepository用 JDBC 实现同样的五个方法。MainFrame 里原本直接调用 CsvUtil 的位置全部改成面向 repository 接口调用。这一步改完后切换存储只需改一行实例化代码窗体层完全不动。6.2 给主窗体加一个消费统计栏如果想在界面上立刻看到改动效果可以在窗体底部加一个 JLabel 统计栏。常见做法是给 Member 增加一个“本月消费”字段在 model 层写一个getTotalBalance()方法汇总全部会员余额界面只负责把这个数字拼到 JLabel 上。这个功能不涉及数据库把统计逻辑放在 model 层可读性会比堆在主界面里好很多。我后来每次改这类 Swing 项目都会提醒自己坚持三条数据变化必须通过 model 通知视图耗时操作必须离开 EDT文件读写永远显式指定编码。这几条虽然朴素但正是从 3.4 节的导出代码一路带到 6.1 节的 repository 设计里项目才不会在翻新过程中翻车。希望这份源码能让你少走一次弯路也希望你改出来的版本比我这个更顺手。本文还有配套的精品资源点击获取
返回列表
PREV
查看更多资讯
NEXT
返回资讯列表