1. 项目概述与核心功能
通讯录管理系统是Java课程设计中非常经典的实战项目,它涵盖了GUI开发、数据库操作、模块化设计等核心知识点。这个项目特别适合刚学完Java基础语法的同学练手,能帮你把零散的知识点串联起来。我当年做第一个Java课设时也选择了类似项目,实测对理解面向对象编程特别有帮助。
这个系统主要实现三大核心模块:
- 用户管理:处理注册和登录逻辑,确保只有授权用户能访问通讯录
- 联系人管理:实现联系人的增删改查(CRUD)操作
- 数据持久化:通过JDBC将数据存储到MySQL数据库
技术选型方面,我们使用Swing做图形界面(比AWT更现代),MySQL作为数据库(免费且轻量),JDBC进行数据库连接。这些技术栈在企业级应用开发中也很常见,学会后对后续学习JavaEE或Spring框架都有帮助。
2. 开发环境搭建
2.1 基础工具准备
推荐使用IntelliJ IDEA社区版(免费)作为开发工具,比Eclipse对新手更友好。我实测在配置环境时能少踩很多坑。还需要安装:
- JDK 1.8+(建议用Oracle JDK)
- MySQL 5.7+(注意配置字符集为utf8mb4)
- Navicat或DBeaver(可视化操作数据库)
# 检查Java环境(应显示1.8+) java -version # 启动MySQL服务(Linux/Mac) sudo service mysql start2.2 创建Maven项目
在IDEA中新建Maven项目,添加以下依赖到pom.xml:
<dependencies> <!-- MySQL驱动 --> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>8.0.28</version> </dependency> <!-- 单元测试 --> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.13.2</version> <scope>test</scope> </dependency> </dependencies>2.3 数据库设计
创建名为address_book的数据库,然后执行以下SQL:
CREATE TABLE users ( id INT PRIMARY KEY AUTO_INCREMENT, username VARCHAR(50) UNIQUE NOT NULL, password VARCHAR(100) NOT NULL, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ); CREATE TABLE contacts ( id INT PRIMARY KEY AUTO_INCREMENT, user_id INT NOT NULL, name VARCHAR(50) NOT NULL, phone VARCHAR(20), email VARCHAR(50), company VARCHAR(100), created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, FOREIGN KEY (user_id) REFERENCES users(id) );提示:建议给phone和email字段添加索引,能显著提升搜索性能。我在实际项目中发现,当联系人超过5000条时,无索引的模糊查询会变得很慢。
3. 登录注册模块实现
3.1 登录界面设计
使用Swing的JFrame和JPanel构建登录窗口。关键点在于:
- 使用GridBagLayout进行组件布局(比绝对定位更灵活)
- 为密码框使用JPasswordField
- 添加焦点监听实现回车键登录
public class LoginFrame extends JFrame { private JTextField usernameField; private JPasswordField passwordField; public LoginFrame() { setTitle("通讯录管理系统 - 登录"); setSize(400, 300); setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); JPanel panel = new JPanel(new GridBagLayout()); GridBagConstraints gbc = new GridBagConstraints(); gbc.insets = new Insets(5, 5, 5, 5); // 用户名标签和输入框 gbc.gridx = 0; gbc.gridy = 0; panel.add(new JLabel("用户名:"), gbc); gbc.gridx = 1; gbc.gridy = 0; gbc.fill = GridBagConstraints.HORIZONTAL; usernameField = new JTextField(15); panel.add(usernameField, gbc); // 密码标签和输入框 gbc.gridx = 0; gbc.gridy = 1; panel.add(new JLabel("密码:"), gbc); gbc.gridx = 1; gbc.gridy = 1; passwordField = new JPasswordField(15); passwordField.addActionListener(e -> attemptLogin()); // 回车触发登录 panel.add(passwordField, gbc); // 登录按钮 gbc.gridx = 0; gbc.gridy = 2; gbc.gridwidth = 2; JButton loginButton = new JButton("登录"); loginButton.addActionListener(e -> attemptLogin()); panel.add(loginButton, gbc); add(panel); setLocationRelativeTo(null); // 窗口居中 } private void attemptLogin() { // 登录逻辑实现 } }3.2 用户认证逻辑
创建UserService处理数据库操作,注意要使用PreparedStatement防止SQL注入:
public class UserService { public static boolean login(String username, String password) { String sql = "SELECT password FROM users WHERE username = ?"; try (Connection conn = DBUtil.getConnection(); PreparedStatement stmt = conn.prepareStatement(sql)) { stmt.setString(1, username); ResultSet rs = stmt.executeQuery(); if (rs.next()) { String storedPassword = rs.getString("password"); return storedPassword.equals(hashPassword(password)); } } catch (SQLException e) { e.printStackTrace(); } return false; } private static String hashPassword(String plain) { // 实际项目应该用BCrypt等安全哈希算法 return plain; // 示例简化处理 } }3.3 注册功能实现
注册时需要检查用户名是否已存在,并确认两次密码一致:
public static boolean register(String username, String password, String confirmPassword) { if (!password.equals(confirmPassword)) { return false; } String checkSql = "SELECT id FROM users WHERE username = ?"; String insertSql = "INSERT INTO users(username, password) VALUES(?, ?)"; try (Connection conn = DBUtil.getConnection(); PreparedStatement checkStmt = conn.prepareStatement(checkSql); PreparedStatement insertStmt = conn.prepareStatement(insertSql)) { // 检查用户名是否存在 checkStmt.setString(1, username); ResultSet rs = checkStmt.executeQuery(); if (rs.next()) { return false; // 用户名已存在 } // 插入新用户 insertStmt.setString(1, username); insertStmt.setString(2, hashPassword(password)); return insertStmt.executeUpdate() > 0; } catch (SQLException e) { e.printStackTrace(); return false; } }4. 通讯录核心功能开发
4.1 主界面设计
主界面采用经典的BorderLayout:
- 北部:搜索栏
- 中部:JTable展示联系人
- 南部:操作按钮
public class ContactsFrame extends JFrame { private JTable contactsTable; private DefaultTableModel tableModel; public ContactsFrame(int userId) { setTitle("通讯录管理系统"); setSize(800, 600); setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); // 表格模型 tableModel = new DefaultTableModel(new Object[]{"ID", "姓名", "电话", "邮箱", "公司"}, 0); contactsTable = new JTable(tableModel); // 搜索面板 JPanel searchPanel = new JPanel(); JTextField searchField = new JTextField(20); JButton searchButton = new JButton("搜索"); searchButton.addActionListener(e -> searchContacts(searchField.getText())); searchPanel.add(searchField); searchPanel.add(searchButton); // 操作按钮 JPanel buttonPanel = new JPanel(); JButton addButton = new JButton("添加"); JButton editButton = new JButton("编辑"); JButton deleteButton = new JButton("删除"); buttonPanel.add(addButton); buttonPanel.add(editButton); buttonPanel.add(deleteButton); // 布局 add(searchPanel, BorderLayout.NORTH); add(new JScrollPane(contactsTable), BorderLayout.CENTER); add(buttonPanel, BorderLayout.SOUTH); loadContacts(userId); } private void loadContacts(int userId) { // 加载联系人数据 } }4.2 联系人增删改查
实现ContactService处理业务逻辑:
public class ContactService { public static boolean addContact(int userId, String name, String phone, String email, String company) { String sql = "INSERT INTO contacts(user_id, name, phone, email, company) VALUES(?, ?, ?, ?, ?)"; try (Connection conn = DBUtil.getConnection(); PreparedStatement stmt = conn.prepareStatement(sql)) { stmt.setInt(1, userId); stmt.setString(2, name); stmt.setString(3, phone); stmt.setString(4, email); stmt.setString(5, company); return stmt.executeUpdate() > 0; } catch (SQLException e) { e.printStackTrace(); return false; } } public static List<Contact> searchContacts(int userId, String keyword) { List<Contact> contacts = new ArrayList<>(); String sql = "SELECT * FROM contacts WHERE user_id = ? AND (name LIKE ? OR phone LIKE ?)"; try (Connection conn = DBUtil.getConnection(); PreparedStatement stmt = conn.prepareStatement(sql)) { stmt.setInt(1, userId); stmt.setString(2, "%" + keyword + "%"); stmt.setString(3, "%" + keyword + "%"); ResultSet rs = stmt.executeQuery(); while (rs.next()) { Contact contact = new Contact(); contact.setId(rs.getInt("id")); contact.setName(rs.getString("name")); contact.setPhone(rs.getString("phone")); contact.setEmail(rs.getString("email")); contact.setCompany(rs.getString("company")); contacts.add(contact); } } catch (SQLException e) { e.printStackTrace(); } return contacts; } }4.3 数据库工具类
DBUtil封装通用数据库操作:
public class DBUtil { private static final String URL = "jdbc:mysql://localhost:3306/address_book?useSSL=false"; private static final String USER = "root"; private static final String PASSWORD = "123456"; static { try { Class.forName("com.mysql.cj.jdbc.Driver"); } catch (ClassNotFoundException e) { e.printStackTrace(); } } public static Connection getConnection() throws SQLException { return DriverManager.getConnection(URL, USER, PASSWORD); } public static void close(Connection conn, Statement stmt, ResultSet rs) { try { if (rs != null) rs.close(); if (stmt != null) stmt.close(); if (conn != null) conn.close(); } catch (SQLException e) { e.printStackTrace(); } } }5. 项目优化与扩展
5.1 输入验证
对所有用户输入进行校验:
public static boolean isValidPhone(String phone) { return phone.matches("^1[3-9]\\d{9}$"); // 简单手机号验证 } public static boolean isValidEmail(String email) { return email.matches("^\\w+@[a-zA-Z_]+?\\.[a-zA-Z]{2,3}$"); }5.2 数据分页
当联系人数量多时,实现分页查询:
public static List<Contact> getContactsByPage(int userId, int page, int pageSize) { List<Contact> contacts = new ArrayList<>(); String sql = "SELECT * FROM contacts WHERE user_id = ? LIMIT ? OFFSET ?"; try (Connection conn = DBUtil.getConnection(); PreparedStatement stmt = conn.prepareStatement(sql)) { stmt.setInt(1, userId); stmt.setInt(2, pageSize); stmt.setInt(3, (page - 1) * pageSize); ResultSet rs = stmt.executeQuery(); while (rs.next()) { // 组装Contact对象 } } catch (SQLException e) { e.printStackTrace(); } return contacts; }5.3 导出功能
添加导出为Excel的功能:
public static boolean exportToExcel(int userId, String filePath) { List<Contact> contacts = getAllContacts(userId); try (Workbook workbook = new XSSFWorkbook()) { Sheet sheet = workbook.createSheet("通讯录"); // 创建表头 Row headerRow = sheet.createRow(0); headerRow.createCell(0).setCellValue("姓名"); headerRow.createCell(1).setCellValue("电话"); // 其他列... // 填充数据 for (int i = 0; i < contacts.size(); i++) { Row row = sheet.createRow(i + 1); Contact contact = contacts.get(i); row.createCell(0).setCellValue(contact.getName()); row.createCell(1).setCellValue(contact.getPhone()); // 其他字段... } try (FileOutputStream fos = new FileOutputStream(filePath)) { workbook.write(fos); return true; } } catch (IOException e) { e.printStackTrace(); return false; } }6. 常见问题解决
6.1 中文乱码问题
确保数据库连接URL添加字符集参数:
jdbc:mysql://localhost:3306/address_book?useUnicode=true&characterEncoding=UTF-86.2 界面卡顿
耗时操作(如数据库查询)应该在SwingWorker中执行:
new SwingWorker<List<Contact>, Void>() { @Override protected List<Contact> doInBackground() throws Exception { return ContactService.getAllContacts(userId); } @Override protected void done() { try { updateTable(get()); } catch (Exception e) { e.printStackTrace(); } } }.execute();6.3 数据库连接池
生产环境建议使用HikariCP等连接池:
<dependency> <groupId>com.zaxxer</groupId> <artifactId>HikariCP</artifactId> <version>5.0.1</version> </dependency>public class DataSource { private static final HikariConfig config = new HikariConfig(); private static final HikariDataSource ds; static { config.setJdbcUrl("jdbc:mysql://localhost:3306/address_book"); config.setUsername("root"); config.setPassword("123456"); config.addDataSourceProperty("cachePrepStmts", "true"); config.addDataSourceProperty("prepStmtCacheSize", "250"); ds = new HikariDataSource(config); } public static Connection getConnection() throws SQLException { return ds.getConnection(); } }