
简介这份资源是面向Java初学者与课程设计选课学生的通讯录管理系统完整项目包对应Java课程设计综合题要求采用JDBC连接数据库界面可选用Swing GUI或JSP技术实现。系统需完成用户注册登录以及通讯录信息的浏览、查找与增删改维护适合用来练习类的设计、继承与多态等面向对象思想并熟悉窗体、面板、按钮、文本框等Swing组件的事件处理与用户交互。压缩包为rar格式共5个文件包含4个java源码与1个sql脚本分别承担界面逻辑、数据库连接工具与建表数据初始化等职责整体约8KB结构精简便于快速阅读与二次开发。目前已有1562人学习下载读者可借此掌握JDBC连接MySQL、数据库表结构设计及增删改查操作理解模块化拆分思路并作为课程设计参考方案或桌面应用入门练手项目使用。1. 从一份“能跑起来”的通讯录说起为什么我建议你亲手拆一遍很多同学做 Java 课程设计第一反应是去网上找一份“能跑就行”的源码改个名字、换个配色就交差。但真到答辩或者二次开发的时候问题就来了联系人怎么存的增删改查为什么一重启就丢数据界面和逻辑为什么缠在一起改不动我见过太多人卡在这一步最后只能重写。这份通讯录管理系统就是典型的“麻雀虽小五脏俱全”——它把 Java 基础、集合框架、文件持久化、Swing 界面、事件监听这几块串成了一条完整的链路。你把它跑通等于把课程设计里最常考的 CRUD、IO 流、GUI 三大块一次性过了一遍。适合谁刚学完 Java 基础、想做第一个完整桌面项目的同学以及需要一份干净骨架去二次开发的人。下面我按“先跑通、再拆解、最后改造”的顺序把这份源码讲透。2. 项目骨架与运行环境从目录结构到第一个可执行窗口2.1 目录结构三层分包各司其职拿到源码先别急着点运行花两分钟看目录。这份通讯录通常采用经典的三层结构包名可能叫com.contacts或addressbook但分层逻辑是一致的src/ ├── main/ │ ├── java/ │ │ ├── entity/ # 实体类Contact 定义字段 │ │ ├── dao/ # 数据访问层负责读写文件 │ │ ├── service/ # 业务逻辑层校验、去重 │ │ ├── ui/ # Swing 界面主窗口和弹窗 │ │ └── Main.java # 程序入口 │ └── resources/ │ └── contacts.dat # 数据文件运行时生成entity包只放一个Contact类字段一般是id、name、phone、email、group。dao包是核心负责把内存里的ListContact序列化到磁盘。service包做业务校验比如手机号格式、姓名非空。ui包用 Swing 画表格和按钮。这种分法的好处是你想换数据库只动dao想换界面只动ui业务规则变了只改service。新手最容易犯的错是把所有代码塞进一个Main.java改一处崩三处。2.2 环境准备JDK 版本与 IDE 选择这份项目对 JDK 版本不挑JDK 8 到 JDK 17 都能跑。我一般用 JDK 11因为 Swing 在 11 上稳定且var关键字能用但项目里基本没用到。IDE 用 IntelliJ IDEA 社区版就够Eclipse 也行。新建项目时注意源码编码统一设成 UTF-8否则中文姓名会变乱码——这是血泪经验我见过有人因为 GBK 和 UTF-8 混用联系人名字全成了问号。导入步骤打开 IDEA选择File - New - Project from Existing Sources。指向源码根目录IDEA 会自动识别src为源码根。检查Project Structure - Modules确认src/main/java被标记为 Sources。如果项目用了 Mavenpom.xml里通常只有junit测试依赖没有额外第三方库直接刷新即可。提示如果导入后Main.java报红多半是包名和目录不匹配。检查package声明是否和文件夹路径一致。2.3 跑起来入口类与启动参数入口类Main.java通常只有几行public class Main { public static void main(String[] args) { // 设置外观为系统默认避免 Windows 和 Mac 下按钮风格差异过大 try { UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName()); } catch (Exception e) { e.printStackTrace(); } // 启动主窗口Swing 要求 UI 操作在事件分发线程中执行 SwingUtilities.invokeLater(() - new MainFrame().setVisible(true)); } }逻辑说明UIManager.setLookAndFeel让界面跟随操作系统Windows 下是 Windows 风格Mac 下是 Aqua 风格避免“土味”默认 Metal 外观。SwingUtilities.invokeLater是 Swing 的硬性要求——所有 UI 创建和更新必须在事件分发线程EDT里做否则会出现界面卡死或组件闪烁。参数方面MainFrame构造函数里一般会初始化表格模型、加载数据文件、绑定按钮事件。跑起来后你会看到一个带表格的窗口顶部有搜索框底部有增删改查按钮。如果窗口空白检查contacts.dat是否存在——首次运行文件不存在是正常的代码里应该有if (!file.exists())的判断直接返回空列表。3. 核心模块拆解联系人增删改查与文件持久化链路3.1 实体类 Contact字段设计与序列化准备Contact类是整个系统的数据单元字段设计直接决定后续功能边界public class Contact implements Serializable { private static final long serialVersionUID 1L; // 序列化版本号防止反序列化失败 private String id; // 唯一标识一般用 UUID 或时间戳 private String name; // 姓名 private String phone; // 手机号 private String email; // 邮箱 private String group; // 分组如“同学”“同事” // 无参构造反序列化时需要 public Contact() {} // 全参构造方便快速创建 public Contact(String id, String name, String phone, String email, String group) { this.id id; this.name name; this.phone phone; this.email email; this.group group; } // getter/setter 省略但必须齐全否则 Swing 表格模型取不到值 Override public String toString() { return name - phone; } }逻辑说明实现Serializable是为了后面用对象流直接写文件。serialVersionUID建议显式声明否则你改一个字段旧数据文件就反序列化失败报InvalidClassException。id用UUID.randomUUID().toString()生成保证唯一。toString重写是为了在JList或下拉框里直接显示姓名和电话。参数方面group字段可以做成枚举但课程设计里用字符串更灵活方便你后续加“家人”“客户”等分类。3.2 DAO 层文件读写与集合操作DAO 层是这份项目的灵魂负责把ListContact和磁盘文件互相转换。常见做法是用ObjectOutputStream和ObjectInputStreampublic class ContactDao { private static final String FILE_PATH contacts.dat; // 读取所有联系人 SuppressWarnings(unchecked) public ListContact findAll() { File file new File(FILE_PATH); if (!file.exists()) { return new ArrayList(); // 文件不存在返回空列表不抛异常 } try (ObjectInputStream ois new ObjectInputStream(new FileInputStream(file))) { return (ListContact) ois.readObject(); } catch (IOException | ClassNotFoundException e) { e.printStackTrace(); return new ArrayList(); } } // 保存所有联系人全量覆盖 public void saveAll(ListContact contacts) { try (ObjectOutputStream oos new ObjectOutputStream(new FileOutputStream(FILE_PATH))) { oos.writeObject(contacts); } catch (IOException e) { e.printStackTrace(); } } }逻辑说明findAll先判断文件是否存在不存在直接返回空列表避免FileNotFoundException。try-with-resources保证流自动关闭不用手写finally。saveAll采用全量覆盖策略——每次增删改后把整个List写回文件。这种做法的优点是实现简单缺点是数据量大时性能差。课程设计里联系人一般几十到几百条全量写完全够用。参数方面FILE_PATH可以改成System.getProperty(user.home) /contacts.dat这样数据存在用户目录下换项目路径也不会丢。3.3 Service 层业务校验与去重逻辑Service 层夹在 DAO 和 UI 之间负责“脏活累活”public class ContactService { private final ContactDao dao new ContactDao(); public ListContact getAll() { return dao.findAll(); } public boolean add(Contact contact) { // 校验必填字段 if (contact.getName() null || contact.getName().trim().isEmpty()) { return false; } // 校验手机号格式11 位数字 if (!contact.getPhone().matches(\\d{11})) { return false; } ListContact list dao.findAll(); // 去重手机号相同视为重复 for (Contact c : list) { if (c.getPhone().equals(contact.getPhone())) { return false; } } list.add(contact); dao.saveAll(list); return true; } public boolean delete(String id) { ListContact list dao.findAll(); boolean removed list.removeIf(c - c.getId().equals(id)); if (removed) { dao.saveAll(list); } return removed; } public boolean update(Contact contact) { ListContact list dao.findAll(); for (int i 0; i list.size(); i) { if (list.get(i).getId().equals(contact.getId())) { list.set(i, contact); dao.saveAll(list); return true; } } return false; } }逻辑说明add方法先做非空和格式校验再用手机号去重。delete用removeIf按id删除Java 8 的 Lambda 写起来很干净。update遍历找到相同id后替换。注意每次修改后都调用dao.saveAll保证内存和磁盘一致。参数方面手机号正则\\d{11}只匹配 11 位数字如果你要支持国际号码改成\\?\\d{7,15}。返回boolean是为了让 UI 层知道操作是否成功弹对应的提示框。3.4 UI 层Swing 表格模型与事件绑定UI 层用JTable展示数据核心是AbstractTableModelpublic class ContactTableModel extends AbstractTableModel { private final String[] columns {姓名, 电话, 邮箱, 分组}; private ListContact data new ArrayList(); public void setData(ListContact data) { this.data data; fireTableDataChanged(); // 通知表格刷新 } Override public int getRowCount() { return data.size(); } Override public int getColumnCount() { return columns.length; } Override public String getColumnName(int column) { return columns[column]; } Override public Object getValueAt(int rowIndex, int columnIndex) { Contact c data.get(rowIndex); switch (columnIndex) { case 0: return c.getName(); case 1: return c.getPhone(); case 2: return c.getEmail(); case 3: return c.getGroup(); default: return null; } } }逻辑说明AbstractTableModel是 Swing 表格的标准做法你只需要告诉它行数、列数、列名和每个单元格的值。fireTableDataChanged在数据变化后调用表格自动重绘。按钮事件绑定一般写在MainFrame里addButton.addActionListener(e - { // 弹出对话框收集输入 Contact contact showAddDialog(); if (contact ! null service.add(contact)) { tableModel.setData(service.getAll()); // 刷新表格 } else { JOptionPane.showMessageDialog(this, 添加失败请检查输入); } });参数方面showAddDialog返回一个Contact对象或null用户取消。service.add返回false时弹提示。这种“UI 调 ServiceService 调 DAO”的链路清晰改起来不头疼。4. 避坑与排查新手跑这份源码最容易翻车的 5 个点4.1 中文乱码文件编码不统一现象联系人姓名显示为“???”或“锟斤拷”。原因源码文件用 GBK 保存IDE 用 UTF-8 读取或者ObjectOutputStream写入时字符串编码不一致。解决IDEA 右下角把文件编码统一改成 UTF-8Run/Debug Configurations里 VM options 加-Dfile.encodingUTF-8。如果已经写入了乱码数据删掉contacts.dat重新录入。4.2 反序列化失败serialVersionUID 不匹配现象运行报java.io.InvalidClassException: Contact; local class incompatible。原因你改了Contact类的字段但没更新serialVersionUID旧数据文件和新类对不上。解决显式声明private static final long serialVersionUID 1L;改字段时如果不想丢数据保持这个值不变如果字段结构大改删掉旧contacts.dat重新生成。4.3 界面卡死在 EDT 外更新组件现象点击按钮后窗口无响应或者表格数据变了但界面不刷新。原因在非事件分发线程里直接操作JTable或JLabel。解决所有 UI 更新用SwingUtilities.invokeLater(() - { ... })包起来。如果你在后台线程读文件读完后再切回 EDT 刷新表格。4.4 数据丢失全量覆盖写文件的时机现象添加联系人后之前的数据没了。原因saveAll传入的List是空列表或者findAll读文件失败返回了空列表然后你直接add再saveAll把空列表写回去了。解决在add方法里先findAll拿到现有数据再追加再saveAll。不要先清空再写。另外findAll捕获异常后返回空列表是危险的——如果文件损坏你会以为没数据然后覆盖掉。建议异常时返回null并让上层处理。4.5 手机号校验误伤正则太严现象输入13800138000提示格式错误。原因正则写成了^1[3-9]\\d{9}$但没转义或者用了matches却带了^$matches本身全匹配加^$反而可能出错。解决用phone.matches(1[3-9]\\d{9})即可不要加^$。如果要做更宽松的校验用\\d{7,15}。5. 二次开发改造从课程设计到能写进简历的小工具5.1 换持久化方案从文件到 SQLite文件序列化的缺点是查不了、搜不快、并发差。二次开发第一个改造点就是换 SQLite。引入sqlite-jdbc依赖建一张contact表CREATE TABLE contact ( id TEXT PRIMARY KEY, name TEXT NOT NULL, phone TEXT NOT NULL UNIQUE, email TEXT, group_name TEXT );然后把ContactDao里的findAll改成SELECT * FROM contactsaveAll改成批量INSERT OR REPLACE。Service 层几乎不用动这就是分层的好处。改完后搜索功能可以用WHERE name LIKE ?实现比内存遍历快得多。5.2 加导入导出CSV 与 Excel课程设计答辩时老师常问“能不能导入手机通讯录”。加一个 CSV 导出很简单用BufferedWriter按行写name,phone,email,group。导入时用BufferedReader逐行读split(,)后构造Contact。注意处理逗号转义和空行。如果想支持 Excel引入Apache POI但依赖会变大课程设计里 CSV 足够。5.3 界面美化换 FlatLaf 主题Swing 默认外观确实丑。引入com.formdev:flatlaf依赖在Main里加一行FlatLightLaf.setup();界面立刻变成现代扁平风格。再配合UIManager.put(Table.rowHeight, 30)调整行高观感提升明显。这个改动风险低、效果好适合放在答辩演示前做。5.4 验证改造是否成功三个必测用例改完代码别急着交跑三个用例第一添加一个联系人关闭程序再打开数据还在第二添加重复手机号提示失败第三删除一个联系人表格刷新且文件里也没了。这三个过了基本功能就稳了。我一般还会加一个“边界测试”姓名输入空格、手机号输入 10 位看校验是否生效。5.5 一个让我长记性的习惯早期我做课程设计改完 DAO 层直接跑结果把测试数据全覆盖了只能重新录。从那以后我每次动持久化代码前都强制把contacts.dat备份一份到contacts.dat.bak跑通了再删备份。这个习惯后来做任何涉及数据写入的功能都保留着——先备份再改再验证。希望这份拆解能帮你少走点弯路把这份通讯录真正变成自己的项目。本文还有配套的精品资源点击获取