简介面向Java课程设计或JDBC入门学习者这份资源给出通讯录管理系统的完整实现涵盖用户注册登录、通讯录浏览、查找与增删改维护等核心功能。项目采用MySQL数据库并通过JDBC连接界面基于Swing GUI设计在代码中体现类的封装、继承与多态等面向对象思想同时涉及数据库表结构设计与基本增删改查操作可直接作为课程设计参考。资源包为RAR格式体积仅8KB共有5个文件包括4个Java源文件与1个SQL脚本Java源文件覆盖登录窗口、注册窗口、通讯录管理主窗口以及数据库工具类SQL脚本用于建库建表与初始化数据结构简单清晰便于导入开发环境快速运行目前已有1562人学习下载。通过代码可以清晰看到模块化拆分登录注册窗口负责用户认证通讯录主窗口完成联系人维护数据库工具类集中管理JDBC连接SQL脚本提供数据表基础对需要完成同类Java课程设计、复习Swing事件处理或理解JDBC持久化操作的读者提供了一份可直接借鉴的Java项目框架。1. Java课程设计选型通讯录管理系统凭什么成了经典款Java课程设计选什么题能把Java SE阶段的知识点全部串起来又不会被答辩老师挑战到无从招架我的答案是通讯录管理系统。它看起来功能不多但实体关系、JDBC增删改查、Swing事件驱动、分组过滤、输入校验这些Java SE阶段的核心考点一个都绕不开演示时还不需要额外搭环境。这篇笔记基于一套实际跑通的课程设计源码包来拆先讲清楚技术选型为什么这么定再顺着表结构、DAO层、界面层一步步落地最后把最容易翻车的几个点单独拎出来。适合正在选题的在校学生也适合想用几天时间把Java SE JDBC Swing串一遍的入门开发者。2. 系统设计与技术选型三套方案选哪套先想清楚存储2.1 技术栈三选一纯Swing、JavaWeb还是JavaFX通讯录管理系统在网上能搜到的实现版本非常多抛开花哨的框架不谈课程设计场景下主流是三条路Swing JDBC MySQL的桌面程序、JSP Servlet MySQL的Web应用、JavaFX SQLite的现代化桌面应用。先说结论如果课程大纲里讲的是Java SE或者工期只有两周左右直接选Swing JDBC MySQL。理由有三点桌面程序答辩演示最直接双击启动、鼠标操作都在评委眼皮底下不需要准备浏览器和Tomcat的启动时间报错全部打在控制台定位问题时比Web端少绕一层网上的开源课设里Swing版本占大头哪天卡住了搜索“JTable刷新问题”“PreparedStatement怎么用”基本都能搜到现成答案。把三条路放在一起对比选型会更清楚技术方案界面形态数据存储上手成本答辩风险Swing JDBC MySQL桌面窗口MySQL低跟着课设走就行低演示直观JSP Servlet MySQLWeb页面MySQL中要配Tomcat中部署环节容易翻车JavaFX SQLite桌面窗口SQLite单文件中资料相对少低但评委可能不熟JavaWeb方案不是不能选但课设时间本来就紧还要处理Servlet映射、请求转发、session管理这些额外复杂度。如果没有任何Web基础答辩前光是调试一个404就能耗掉一下午。JavaFX界面确实好看可网上的课设资料比Swing少一个量级碰到冷门报错容易卡住。所以核心判断标准就一条把时间和精力留给业务代码不要耗在环境搭建上。2.2 数据持久化路线文件、SQLite还是MySQL存储层是答辩时被问得最多的地方。最偷懒的做法是把联系人写进TXT文件每行一个联系人但这样做有几个问题不支持按姓名/电话查询程序崩溃容易丢数据而且答辩老师一句“如果有一万条联系人你怎么查”就接不上来了。我见过不少同学卡在存储选型上其实思路很简单本地装了MySQL就选MySQL没装就用SQLite。两者都可以挂同一套DAO接口后面第3章会看到这个设计的好处。文件存储可以作为“导出备份”功能出现但不要作为唯一的数据落点。三种方案各自的边界文件存储实现最快但只适合做备份导出不适合做主存储。字符串拼接容易把字段搞错位序列化文件换一台电脑跑可能直接反序列化失败。SQLite单文件嵌入式数据库JDBC驱动一个jar包就能跑。缺点是SQL语法和MySQL有细微差别网上搜到的报错可能对不上。MySQL最接近生产环境PreparedStatement的写法、外键约束、事务控制都能练到。缺点是安装配置这一步本身就是一个坑时区和端口问题后面避坑章会专门讲。2.3 功能模块与数据库表结构设计功能拆解这一步别省直接在表格里列出来模块功能点涉及技术联系人管理新增、修改、删除、列表展示PreparedStatement、JTable分组管理分组列表、按组加载联系人外键关联、级联处理搜索按姓名/电话模糊查询LIKE语句或内存过滤辅助功能联系人计数、CSV导出SQL聚合函数、文件IO数据库设计是整份代码里最值得认真对待的部分。通讯录看起来只有联系人但实际要维护的是两张表分组表t_group和联系人表t_contact。联系人通过group_id外键关联到分组。SQL脚本我习惯放在项目根目录的sql文件夹里带一个建库语句方便在任何机器上重建环境CREATE DATABASE IF NOT EXISTS address_book DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci; USE address_book; CREATE TABLE t_group ( id INT PRIMARY KEY AUTO_INCREMENT, name VARCHAR(50) NOT NULL UNIQUE, description VARCHAR(200) ); CREATE TABLE t_contact ( id INT PRIMARY KEY AUTO_INCREMENT, group_id INT, name VARCHAR(50) NOT NULL, phone VARCHAR(20), email VARCHAR(100), remark VARCHAR(200), create_time DATETIME DEFAULT CURRENT_TIMESTAMP, CONSTRAINT fk_group FOREIGN KEY (group_id) REFERENCES t_group(id) ON DELETE SET NULL ) ENGINEInnoDB DEFAULT CHARSETutf8mb4;这块有两个参数值得多说一句。字符集用了utf8mb4而不是utf8是因为MySQL里utf8是utf8mb3的别名最多存3字节某些生僻字和emoji会直接报错课设阶段可能用不到但答辩老师看到这个细节会高看一眼。外键的ON DELETE SET NULL含义是删除分组时联系人保留但group_id置空。这样用户误删分组不会连带清空联系人数据如果改成ON DELETE CASCADE删个分组联系人全没了演示时手一滑就翻车。表引擎选InnoDB而不是MyISAM是为了让外键约束真正生效MyISAM根本不检查外键。注意如果最终选了SQLite而不是MySQL上面的建表语句要改三处——去掉ENGINEInnoDB、把AUTO_INCREMENT改成AUTOINCREMENTcreate_time通过程序传入当前时间。3. 从实体类到DAO层把通讯录的骨架先立起来3.1 实体类设计字段类型先想好后面少改一次很多第一次做课设的人习惯在鼠标点击事件里直接拼SQL比如在JButton的监听器里写“INSERT INTO...”。这样做课设能做出来但一旦需求变了比如要加一个“生日”字段你得把所有涉及SQL的地方全部改一遍。我的建议是多花半小时把三层结构立起来实体类entity- 数据访问对象DAO- 界面view。后面每一步都会轻松很多。先看Contact实体类。这里有个容易被忽略的细节联系人的“所属分组”允许为空所以groupId用了Integer而不是int。用int的话数据库里为NULL时JDBC驱动会直接报错。实体类字段直接对应数据库列命名上用驼峰风格跟表的snake_case对应靠自己映射而不是靠框架自动转换// Contact.java 联系人实体类 public class Contact { private Integer id; // 数据库自增主键新增时由数据库生成 private Integer groupId; // 所属分组 id允许为空用包装类 Integer private String name; // 姓名必填字段界面层负责校验 private String phone; // 手机号存字符串而不是数字 private String email; // 邮箱 private String remark; // 备注 private String createTime; // 创建时间用字符串形式便于表格直接显示 public Contact() {} public Contact(Integer id, Integer groupId, String name, String phone, String email, String remark, String createTime) { this.id id; this.groupId groupId; this.name name; this.phone phone; this.email email; this.remark remark; this.createTime createTime; } // getter / setter 由 IDE 自动生成这里省略 }两个参数选择说明一下phone为什么用String电话号码如果需要显示为010开头或86开头用int会丢掉前导0而且号码长度在20字以内字符串在数据库里用VARCHAR存查询匹配也更自然。createTime为什么也用String数据库里的DATETIME取出后在Java里转成java.util.Date再格式化显示中间多一道SimpleDateFormat时区不对容易出偏差。直接用DATE_FORMAT函数在SQL层转成字符串省掉一个麻烦。3.2 DAO接口先定好存储换成SQLite也不用改界面DAO层用接口加实现的方式拆开是这套课设代码里性价比最高的一个设计。接口定义业务方法实现类写具体SQL。这样Swing界面只认接口哪天想把MySQL换成SQLite只需要再写一个实现类界面一行都不用动。// ContactDao.java 联系人数据访问接口 public interface ContactDao { ListContact findAll(); // 查询全部联系人 Contact findById(int id); // 按ID查单个联系人 ListContact search(String keyword); // 按姓名或电话模糊搜索 int add(Contact contact); // 新增返回影响行数 int update(Contact contact); // 修改 int delete(int id); // 删除 }接口的方法签名把业务边界都框死了findAll主要给表格初始化用search给搜索框用add、update、delete的返回值是影响行数能用来判断操作是否成功。主界面里就是调用dao.add(contact)然后刷新表格跟SQL语法完全分开。实现类里最值得看的是findAll方法它展示了三个常见写法// ContactDaoImpl.java 基于 JDBC 的实现类核心代码 public class ContactDaoImpl implements ContactDao { Override public ListContact findAll() { ListContact list new ArrayList(); String sql SELECT id, group_id, name, phone, email, remark, DATE_FORMAT(create_time, %Y-%m-%d %H:%i:%s) AS create_time FROM t_contact ORDER BY id DESC; try (Connection conn DBUtil.getConnection(); PreparedStatement ps conn.prepareStatement(sql); ResultSet rs ps.executeQuery()) { while (rs.next()) { Contact c new Contact(); c.setId(rs.getInt(id)); c.setGroupId((Integer) rs.getObject(group_id)); c.setName(rs.getString(name)); c.setPhone(rs.getString(phone)); c.setEmail(rs.getString(email)); c.setRemark(rs.getString(remark)); c.setCreateTime(rs.getString(create_time)); list.add(c); } } catch (SQLException e) { e.printStackTrace(); } return list; } // add / update / delete 用 PreparedStatement 的 setString / setInt 填充参数 // 套路一致不再重复贴代码 }三个细节解释一下SQL拼接为什么要写两行字符串而不是一行纯属可读性考虑字符串在编译期会拼成一条完整语句不影响执行。PreparedStatement和Statement的区别PreparedStatement会把SQL骨架先预编译参数用setString、setInt填充既避免SQL注入也不用费劲处理字符串里的引号转义。rs.getObject(group_id)而不是getInt是因为该字段可能为NULLgetInt会把NULL转成00在业务上表示“默认分组”容易误判用getObject取出后强转成Integer能保留NULL语义。所有资源都在try-with-resources里自动关闭Connection、PreparedStatement、ResultSet三个对象会在代码块结束后顺序关闭。这个写法在课设里非常加分比在finally里手动关闭少写一堆样板代码。3.3 数据库连接工具类一台机器跑通的参数都是这样磨出来的每次连接数据库的代码不应该散落各处用一个DBUtil集中管理URL、用户名、密码是课设和真实项目都通用的习惯。同时把连接参数写清楚能省掉第二位同学在自己机器上折腾半天的时间。// DBUtil.java 数据库连接工具类 public class DBUtil { private static final String URL jdbc:mysql://localhost:3306/address_book ?useUnicodetruecharacterEncodingutf8 serverTimezoneAsia/Shanghai; private static final String USER root; private static final String PASSWORD 改成你自己的数据库密码; static { try { // MySQL 8.x 推荐用这个驱动类 Class.forName(com.mysql.cj.jdbc.Driver); } catch (ClassNotFoundException e) { e.printStackTrace(); } } public static Connection getConnection() throws SQLException { // 每调用一次返回一个新连接用完由调用方关闭 return DriverManager.getConnection(URL, USER, PASSWORD); } }连接URL里的三个参数是本套代码能跑起来的关键。characterEncodingutf8解决的是中文字符写入和读取的编码一致性没有它会出现“中文看得见但搜不到”这类诡异问题。serverTimezoneAsia/Shanghai解决的是MySQL 8的时区报错。MySQL 8默认serverTimezone是系统时区驱动在连接时会校验国内很多机器上会报一个“中国标准时间无法识别”的错加上这个参数一了百了。驱动类名com.mysql.cj.jdbc.Driver对应mysql-connector-java 8.x版本。如果用的是5.x的jar驱动类名要改成com.mysql.jdbc.Driver这个坑非常经典避坑章会专门讲。如果选SQLite路线DBUtil的差别在于URL变成jdbc:sqlite:address_book.db不需要用户名密码驱动类变成org.sqlite.JDBC。接口不变替换实现类就完事。4. 界面层与交互逻辑Swing组件选对代码少一半4.1 主界面布局JFrame加JTable的经典组合Swing界面不追求华丽但求演示顺手。这套课设的主界面用的是最稳妥的组合JFrame作为主窗口NORTH区域放工具栏搜索框、分组下拉框、新增按钮CENTER区域放JTableSOUTH区域放状态栏。JTable本身不存数据数据在TableModel里。操作表格其实就是操作TableModel这个理解是后面避坑章里“删除不刷新”问题的关键。// MainFrame.java 主界面核心代码 public class MainFrame extends JFrame { private JTable contactTable; private DefaultTableModel tableModel; private ContactDao contactDao new ContactDaoImpl(); public MainFrame() { setTitle(通讯录管理系统); setSize(900, 600); setLocationRelativeTo(null); // 居中显示 setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); initUI(); loadData(); } private void initUI() { // 定义表格列第二参数 0 表示当前无数据行 tableModel new DefaultTableModel( new Object[]{ID, 姓名, 电话, 邮箱, 分组, 备注}, 0); contactTable new JTable(tableModel); // 禁止直接双击编辑单元格编辑操作统一走对话框 contactTable.setDefaultEditor(Object.class, null); // 单选一行配合删除按钮更安全 contactTable.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); // 让表格占满窗口中间区域 add(new JScrollPane(contactTable), BorderLayout.CENTER); } // 从数据库加载全量数据并刷新表格 private void loadData() { refreshTable(contactDao.findAll()); } // 清空表格模型后重新填充 private void refreshTable(ListContact contacts) { tableModel.setRowCount(0); // 旧数据全部清掉 for (Contact c : contacts) { tableModel.addRow(new Object[]{ c.getId(), c.getName(), c.getPhone(), c.getEmail(), c.getGroupName(), c.getRemark() }); } } }setDefaultEditor(Object.class, null)可以关掉双击编辑单元格的能力。课程设计里联系人的修改通常弹对话框做表格直改容易把数据改坏而且还要处理单元格编辑事件复杂度不值。setRowCount(0)是刷新表格最快的办法先把模型清空再逐行添加。有些同学直接new一个新的TableModel再set给JTable那样会丢失表头设置还得把列的宽度重新配。如果列需要排序还可以在contactTable上加上TableRowSorter点击表头就能按姓名或电话排序一行代码的事contactTable.setRowSorter(new TableRowSorter(tableModel));这个排序是纯界面层面的不动数据源演示时点一下表头就排好序效果很直观。4.2 对话框处理新增和编辑模态窗口加校验新增和编辑共用一个ContactDialog构造时区分两种模式传进来的editingContact是null就是新增不是null就是编辑并回填原字段。这个复用能少写一个文件。对话框用模态窗口意思是不关闭它就无法操作主窗口正好符合增删改的流程逻辑。JDialog构造器的第二个参数传true就是模态。// ContactDialog.java 新增/编辑联系人对话框 public class ContactDialog extends JDialog { private JTextField nameField new JTextField(12); private JComboBoxString groupCombo new JComboBox(); private boolean confirmed false; // 点保存且校验通过才变成 true public ContactDialog(MainFrame owner, Contact editingContact) { super(owner, true); // true 表示模态窗口 setTitle(editingContact null ? 新增联系人 : 编辑联系人); // 布局代码省略重点是保存按钮点击时先校验 JButton saveBtn new JButton(保存); saveBtn.addActionListener(e - { if (nameField.getText().trim().isEmpty()) { JOptionPane.showMessageDialog(this, 姓名不能为空); return; } if (nameField.getText().trim().length() 50) { JOptionPane.showMessageDialog(this, 姓名不能超过50个字符); return; } // 电话格式校验防止填入乱七八糟的内容 String phone phoneField.getText().trim(); if (!phone.isEmpty() !phone.matches([0-9\\-]{1,20})) { JOptionPane.showMessageDialog(this, 电话格式不正确); return; } confirmed true; dispose(); // 校验通过关闭对话框 }); } public boolean isConfirmed() { return confirmed; } // 主界面通过该 getter 拿到用户在对话框里填的内容 public String getInputName() { return nameField.getText().trim(); } }校验顺序有个讲究先校验姓名是否为空再校验长度最后校验电话格式因为空字符串长度是0两个判断分开写提示更明确。正则^[0-9\-]{1,20}$覆盖了手机号、座机带区号、国际区号86这类常见格式课设里够用了。返回结果的模式用的是isConfirmed加getInputName这种组合。主界面在对话框dispose之后再判断confirmed为true就组装Contact对象调用dao.add(contact)。用回调或变更监听器反而会把代码绕复杂。4.3 搜索实时化DocumentListener代替搜索按钮搜索功能两种实现方式一种是在DAO层写search方法SQL用LIKE查询适合数据量大、要查数据库的场景另一种是小数据量时直接在内存里过滤已经加载的List然后用refreshTable刷新表格不碰数据库。这套课设用的是方式一因为搜索框要同时匹配姓名和电话还想让匹配结果跟着分组下拉框联动。下面用DocumentListener监听搜索框输入实现“输入即搜索”// MainFrame.java 搜索框实时监听 searchField.getDocument().addDocumentListener(new DocumentListener() { Override public void insertUpdate(DocumentEvent e) { doSearch(); } Override public void removeUpdate(DocumentEvent e) { doSearch(); } Override public void changedUpdate(DocumentEvent e) { doSearch(); } }); private void doSearch() { String keyword searchField.getText().trim(); ListContact result contactDao.search(keyword); refreshTable(result); }用DocumentListener而不是给“搜索”按钮加ActionListener体验上最大的区别是用户每敲一个字就过滤一次不用专门去点按钮。如果做的是按钮版本用户输入完还要伸手去找鼠标点搜索演示效果差一截。search方法里注意keyword为空的处理如果文本框被清空LIKE %会全量匹配这种做法在真实项目里有性能隐患但在课设这种几百条数据量的场景够用。更稳妥的写法是在doSearch里判断keyword.isEmpty()时直接调用findAll()避免无效查询。对应DAO实现里的SQL长这样String sql SELECT ... FROM t_contact WHERE name LIKE ? OR phone LIKE ?; ps.setString(1, % keyword %); ps.setString(2, % keyword %);注意LIKE的模糊匹配默认区分大小写对于姓名和电话来说问题不大。如果想忽略大小写在MySQL里可以给字段加COLLATE utf8mb4_general_ci或者用LOWER函数包一层。课设阶段不用纠结知道有这么回事就行。5. 避坑指南通讯录系统开发中最容易翻车的五个场景这一章从我自己跑这套代码、帮同学排查的经历里挑了五个高频翻车点。每一条都按现象、原因、解决的顺序展开排查时可以直接对照。5.1 中文全变问号从编译器到数据库三层一起查现象数据存进去是文字SELECT出来是一排????或者控制台打印SQL中文正常、界面显示乱码。原因编码问题至少要查三层漏掉任何一层都会乱码。一是源文件本身不是UTF-8编码IDE右下角能看到二是数据库连接URL没带characterEncoding参数三是表本身用的默认latin1字符集。解决建表语句里明确写DEFAULT CHARSETutf8mb4URL里带useUnicodetruecharacterEncodingutf8源文件统一转成UTF-8。我自己排过很多次90%的时候是URL参数漏了剩下10%是建表脚本里的charset被MySQL默默忽略了。有一个更隐蔽的点如果用的IDE自带终端启动程序控制台输出乱码往往是IDE的file.encoding设置问题不影响程序运行但影响判断可以在VM options加-Dfile.encodingUTF-8。5.2 点了删除界面上的行还在现象删除按钮点了没反应或者程序重启之后那行才消失。原因JTable显示的数据在TableModel里你调dao.delete()只删了数据库里的记录没有刷新TableModel界面自然不动还有一种原因是selectedRow拿到的是界面行号但删除逻辑写的是按行号删数组根本没映射到数据库ID。解决删除成功后立刻调refreshTable(contactDao.findAll())让表格从数据源重新加载。不要只删除当前行因为新增、修改之后也要重新查一遍统一走刷新路径最省事。更稳的做法在refreshTable填充数据时把联系人的id也放进表格模型的第一列虽然界面上可以隐藏但模型里必须有删除时从表格行数据里取id而不是靠行号猜。5.3 找不到MySQL驱动类先看jar版本再看classpath现象程序一启动就在DBUtil的静态块里报ClassNotFoundException: com.mysql.jdbc.Driver。原因两个来源。一是用的mysql-connector-java版本是8.x驱动类名改成了com.mysql.cj.jdbc.Driver二是驱动jar包在项目中没被添加为依赖或者只在某个文件夹里躺着没构建进classpath。解决先确认引用的jar包版本然后对应改驱动类名。在IDEA里右键jar包选“Add as Library”把它加入项目依赖如果用Maven检查pom.xml里是否遗漏了依赖声明。一个快速判断方法看jar包文件名带8.x的是新驱动带5.x的是老驱动老版本驱动类名才是com.mysql.jdbc.Driver。5.4 连接MySQL 8报时区错误报错信息里有乱码符号现象程序启动时报错摘要里出现The server time zone value ... is unrecognized中间夹着一串乱码字符。原因MySQL 8的驱动启动时会校验数据库服务器的时区如果系统时区不在驱动认可的列表里就抛异常。报错信息里那串乱码其实是“中国标准时间”这几个字的GBK编码被UTF-8错误解码后的展示。解决在连接URL末尾加上serverTimezoneAsia/Shanghai问题直接消失。如果加了仍报错去MySQL客户端执行select now()确认系统时间正常。还有个别机器的系统时区缩写是CST这个缩写有歧义中国标准时间和北美中部标准时间共用CST干脆显式写Asia/Shanghai别用CST。5.5 窗口关闭了控制台红色方块还在现象点右上角X把窗口关了IDEA控制台里的红色“Stop”按钮还亮着程序并没有真正结束。原因Swing程序关闭窗口后如果还有非守护线程在运行JVM不会退出。常见来源是误用了Timer调度后台任务做数据自动刷新或者设了全局快捷键监听没有移除。解决主界面统一用setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE)如果用了Timer做周期性刷新在窗口关闭事件里调用timer.stop()再释放资源。参考代码// WindowListener 写法的简化示意 addWindowListener(new WindowAdapter() { Override public void windowClosing(WindowEvent e) { if (refreshTimer ! null) { refreshTimer.stop(); // 停止后台定时器 } dispose(); System.exit(0); // 兜底退出课设场景允许 } });这一层“兜底exit”在课设里是允许的虽然不够优雅但能保证演示完不会留下卡住的进程。真实项目不建议这么干因为进程里可能还有别的窗口和他方资源。6. 从交作业到拿得出手两个加分项与答辩前的自检6.1 两个加分扩展CSV导出与分组统计课设做到能跑只是及格线想让答辩老师坐直了听你说可以加个辅助功能把当前列表导出成CSV。这个功能耗时半小时却能展示文件读写、字符串转义、思考边界的综合能力。// 导出当前联系人列表到 csv 文件 try (FileWriter writer new FileWriter(contacts.csv, false)) { writer.write(ID,姓名,电话,邮箱,备注\n); for (Contact c : contactList) { writer.write(c.getId() , c.getName() , c.getPhone() , c.getEmail() , c.getRemark() \n); } } catch (IOException e) { e.printStackTrace(); }一个细节如果备注里本身带逗号用上面这段会串列正确做法是给每个字段加英文双引号包裹或者把逗号替换成全角逗号。答辩时被问到“字段内容有逗号怎么办”能答出来是很加分的。另一个加分项是分组统计。SQL层面就是一条GROUP BY语句把t_contact按group_id分组计数界面放一个标签显示“当前联系人总数xx分组数xx”。它把SQL聚合函数和分组传参都带出来了还能自然引出“JOIN分组表获取分组名”的下一步提问。6.2 答辩演示前十分钟的自检清单检查项检查方式失败时怎么办MySQL服务是否已启动直接运行程序看能否连上检查服务并确认端口3306URL里的密码是否改成本机密码打开DBUtil看一眼改完重新编译演示数据是否足够查分组和联系人数量先跑一遍新增屏幕分辨率与字体大小在答辩机器上试运行一次调setFont字号增删改查完整流程跑一遍每步操作看表格和状态栏看控制台报错定位SQL脚本能否在新机器重建库删除数据库后重跑sql文件检查建表语句语法演示路径建议固定成一条启动 - 新增一个联系人 - 搜索这个联系人 - 修改电话 - 按分组筛选 - 删除 - 看分组恢复但联系人还在。这条路径把增删改查、搜索、分组、外键行为全部覆盖到全程不超过两分钟。我第一次做这个课设时把SQL直接写在按钮事件里答辩老师追问“改一个字段要动几处代码”我答不上来回去重构了两天。从那以后我不管写多小的项目都强制先画表关系、定接口、再做界面这个习惯让我后面做这套通讯录只花三天就收工。源码包里的SQL脚本和DAO接口建议先跑通再改顺序反了容易走弯路希望帮到你。本文还有配套的精品资源点击获取
阅读完成 · 觉得有帮助?