尧图网络 高端网站定制 · 原创设计
免费咨询热线
400-888-6620
免费获取方案
Java Swing+MySQL个人财务系统实战:轻量可控的桌面记账方案
简介这是一套基于Java Swing开发的个人财务收支管理系统源码面向Java初学者与C/S架构实践者聚焦GUI编程、MySQL数据库交互及桌面应用完整开发流程。资源采用EclipseJDKMySQL技术栈覆盖用户管理、收支分类、收入/支出CRUD、密码修改与登录等核心功能模块适合用于课程设计、毕业设计或Swing实战训练。压缩包共111个文件含22个可读Java源文件如login.java、shouruadd.java、80个编译后class文件、5个依赖jar包、1个建库脚本javacaiwu.sql及项目配置文件整体大小2.91MB结构清晰便于逐层理解MVC逻辑与数据库连接机制。已有207人学习下载提供完整可运行环境管理员账号admin/admin、数据库配置说明及CS架构典型实现范例助读者快速掌握Java桌面应用从界面设计到数据持久化的全链路开发能力。1. 为什么用 Java Swing MySQL 做个人财务系统反而比 Web 方案更稳、更轻、更可控你是不是也试过花三天搭 Spring Boot Vue 前后端分离的记账 App结果部署到家里旧笔记本上卡成幻灯片或者用 Electron 打包个桌面记账工具一开就吃掉 800MB 内存连 Excel 都比它响应快——这不是你代码写得差是技术选型在「个人财务」这个场景里天然被高估了复杂度。Java Swing MySQL 的 CS 结构恰恰是被低估的务实方案它不依赖浏览器兼容性、不折腾 HTTPS/跨域/CORS、不卷 Docker/K8s、不求并发扛万级用户——它只服务你一个人每天打开、录入几笔收支、查个月度汇总、导出个 CSV。Eclipse 是成熟到骨子里的 Java IDEMySQL 是装机即用的本地数据库Swing 虽老但控件稳定、事件清晰、无 JS 陷阱、无 React 生命周期玄学。我用这套组合落地过 7 个真实家庭财务项目最长一个运行 4 年零崩溃备份只需拷贝一个.db文件和一个config.properties。它不适合做 SaaS但特别适合「不想维护服务器、不想学新框架、只想今天写完明天就能用」的工程师、财务人员、自由职业者。本文不讲理论优势只拆解怎么在 Eclipse 里从零建工程、连上本地 MySQL、用 Swing 搭出可增删改查图表汇总的完整客户端以及——那些让新手在第 3 小时就放弃的隐藏坑。2. 用 Eclipse 新建 Java Project结构设计与依赖管理的底层逻辑2.1 创建标准 Java Project而非 Dynamic Web 或 Maven为什么Eclipse 中新建项目时必须选择 “Java Project”而不是 “Dynamic Web Project” 或 “Maven Project”。原因很实在Dynamic Web Project 会自动引入 Servlet API、WebContent 目录、web.xml 等冗余结构而本系统是纯桌面 CS 架构不需要 Tomcat、不走 HTTP 协议Maven Project 虽好但对单机财务系统属于“杀鸡用牛刀”你不需要版本冲突解决、不需要多模块聚合、不需要pom.xml里堆 20 行dependency——JDBC 驱动、Swing 组件、日期工具类全在 JDK 8 自带或一个mysql-connector-java-8.0.33.jar就够了。提示Eclipse 版本建议用 2021-094.21或 2023-094.29这两个版本对 JDK 11/17 兼容性最稳且内置 SWT 和 JDT 无报错。避免用最新版 Eclipse 2024-06其默认启用 Java 21 的 sealed class 检查会误报 Swing 继承链错误。创建步骤File → New → Java ProjectProject name 填PersonalFinanceSystem不要用中文、空格、下划线JRE 选择 “Use project specific JRE” → 选已安装的 JDK 11 或 JDK 17JDK 8 已停更JDK 17 是 LTS 最佳平衡点取消勾选 “Create module-info.java”Swing 不需要模块化Finish此时项目结构应为PersonalFinanceSystem/ ├── src/ ├── bin/ (自动生成) └── JRE System Library [jdk-17]2.2 手动添加 MySQL JDBC 驱动jar 包放置位置与 Build Path 设置Swing 本身不带数据库连接能力必须显式引入 JDBC 驱动。不要用 Maven 仓库自动下载要手动导入 jar——这是规避网络波动、代理失败、镜像源失效的第一道防线。去 MySQL 官网下载mysql-connector-java-8.0.33.jar对应 MySQL 8.0或mysql-connector-java-5.1.49.jar对应 MySQL 5.7在项目根目录下新建文件夹lib/把 jar 拖入在 Eclipse 中右键项目 →Build Path → Configure Build Path…→Libraries标签页 →Add JARs…→ 选中lib/mysql-connector-java-8.0.33.jar关键一步点击刚添加的 jar →Source attachment→Edit…→ 指向同名-sources.jar若无可跳过但强烈建议下载带 sources 的版本方便调试 SQLException 时直接看驱动源码点击Order and Export标签页 → 勾选该 jar →OK。验证是否生效在src/下新建包util新建类DBConnectionTest.java写以下代码package util; import java.sql.Connection; import java.sql.DriverManager; import java.sql.SQLException; public class DBConnectionTest { public static void main(String[] args) { String url jdbc:mysql://localhost:3306/finance_db?useSSLfalseserverTimezoneAsia/ShanghaiallowPublicKeyRetrievaltrue; String user root; String password your_password; // 实际使用时请勿硬编码密码 try (Connection conn DriverManager.getConnection(url, user, password)) { System.out.println(✅ 数据库连接成功当前数据库 conn.getCatalog()); } catch (SQLException e) { System.err.println(❌ 连接失败 e.getMessage()); e.printStackTrace(); } } }若控制台输出✅ 数据库连接成功说明 JDBC 驱动已正确加载若报java.lang.ClassNotFoundException: com.mysql.cj.jdbc.Driver一定是 Build Path 没勾选Export或 jar 路径不对若报Access denied for user rootlocalhost说明 MySQL 用户权限未开需进 MySQL 命令行执行ALTER USER rootlocalhost IDENTIFIED WITH mysql_native_password BY your_password; FLUSH PRIVILEGES;。2.3 包结构设计按职责分层拒绝 src 下满屏 .javaSwing 项目最容易陷入“所有类堆在 default package”的泥潭。一旦超过 15 个类找 Main 类都得搜三次。我坚持用四层包结构每个包职责清晰、命名直白包名作用典型类举例main启动入口、主窗体生命周期管理MainApp.java,SplashScreen.javaui所有 Swing 界面组件窗体、面板、表格模型MainFrame.java,IncomePanel.java,ExpenseTable.javadao数据访问对象封装 JDBC 操作与数据库表一一映射IncomeDao.java,CategoryDao.java,SummaryDao.javamodel业务实体类纯 POJO字段与数据库列严格对应IncomeRecord.java,ExpenseRecord.java,Category.javautil工具类数据库连接池、日期格式化、金额计算、配置读取DBUtil.java,DateUtils.java,MoneyUtils.java,ConfigLoader.java注意dao包不叫repository或mapper因为这不是 ORM 场景ui不叫view因为 Swing 没有 MVVM 分离面板本身就是 View Controller 混合体model不加Entity后缀避免冗余——IncomeRecord比IncomeRecordEntity更直觉。创建方式右键src→New → Package依次建main,ui,dao,model,util。后续所有类必须归属其职责包内禁止跨包调用非 public 方法如ui.MainFrame不得直接 newdao.IncomeDao()应通过util.DBUtil获取连接后传参。3. MySQL 数据库建模5 张表支撑完整财务逻辑拒绝过度设计3.1 核心表结构设计为什么只用 5 张表而不是 12 张个人财务系统不是银行核心系统。过度分表比如把income_type、expense_type单独建字典表再关联只会增加 JOIN 复杂度、拖慢查询、让新手看不懂 ER 图。我用 5 张表覆盖 95% 场景且全部支持外键约束InnoDB 引擎表名主键关键字段说明categoriesid(PK, AI)name VARCHAR(50) NOT NULL,type ENUM(income,expense) NOT NULL收入/支出分类如“工资”、“餐饮”、“交通”accountsid(PK, AI)name VARCHAR(50) NOT NULL,balance DECIMAL(12,2) DEFAULT 0.00账户如“招商银行卡”、“支付宝”、“现金”incomesid(PK, AI)amount DECIMAL(12,2) NOT NULL,category_id INT,account_id INT,date DATE NOT NULL,note TEXT收入明细category_id和account_id为外键expensesid(PK, AI)amount DECIMAL(12,2) NOT NULL,category_id INT,account_id INT,date DATE NOT NULL,note TEXT支出明细结构同incomessettingskey VARCHAR(100) PKvalue TEXT,description VARCHAR(200)全局配置如default_currency,first_day_of_week,backup_path提示incomes和expenses分开建表而非合并在transactions表里加type字段——这是为后续报表统计省力。SUM(incomes.amount) 和 SUM(expenses.amount) 可直接写不用 WHERE typeincome减少出错概率。建库建表 SQL保存为finance_db.sql用 MySQL Workbench 或命令行执行-- 创建数据库字符集必须 utf8mb4否则微信昵称等 emoji 存不进去 CREATE DATABASE IF NOT EXISTS finance_db CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; USE finance_db; -- 分类表 CREATE TABLE categories ( id INT PRIMARY KEY AUTO_INCREMENT, name VARCHAR(50) NOT NULL, type ENUM(income,expense) NOT NULL, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, UNIQUE KEY uk_name_type (name, type) ); -- 账户表 CREATE TABLE accounts ( id INT PRIMARY KEY AUTO_INCREMENT, name VARCHAR(50) NOT NULL, balance DECIMAL(12,2) DEFAULT 0.00, is_active BOOLEAN DEFAULT TRUE, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ); -- 收入表 CREATE TABLE incomes ( id INT PRIMARY KEY AUTO_INCREMENT, amount DECIMAL(12,2) NOT NULL, category_id INT NOT NULL, account_id INT NOT NULL, date DATE NOT NULL, note TEXT, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, FOREIGN KEY (category_id) REFERENCES categories(id) ON DELETE CASCADE, FOREIGN KEY (account_id) REFERENCES accounts(id) ON DELETE CASCADE, INDEX idx_date (date), INDEX idx_category (category_id) ); -- 支出表 CREATE TABLE expenses ( id INT PRIMARY KEY AUTO_INCREMENT, amount DECIMAL(12,2) NOT NULL, category_id INT NOT NULL, account_id INT NOT NULL, date DATE NOT NULL, note TEXT, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, FOREIGN KEY (category_id) REFERENCES categories(id) ON DELETE CASCADE, FOREIGN KEY (account_id) REFERENCES accounts(id) ON DELETE CASCADE, INDEX idx_date (date), INDEX idx_category (category_id) ); -- 配置表 CREATE TABLE settings ( key VARCHAR(100) PRIMARY KEY, value TEXT, description VARCHAR(200), updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP ); -- 插入默认数据 INSERT INTO categories (name, type) VALUES (工资, income), (理财收益, income), (兼职, income), (餐饮, expense), (交通, expense), (购物, expense), (房租, expense); INSERT INTO accounts (name, balance) VALUES (招商银行卡, 5000.00), (支付宝, 2000.00), (现金, 800.00); INSERT INTO settings (key, value, description) VALUES (default_currency, CNY, 默认货币单位), (first_day_of_week, 1, 1周一7周日), (backup_path, C:/finance_backup/, 本地备份路径);3.2 连接字符串与安全配置绕过 SSL 报错、时区错乱、公钥检索失败MySQL 8.0 默认强制 SSL 连接而本地开发环境通常没配证书直接导致SQLException: Could not create connection to database server.。同时JDBC URL 若不指定时区Java 读出的DATE字段可能比数据库晚 8 小时因 MySQL 服务端时区为SYSTEM而 JVM 时区为Asia/Shanghai但 JDBC 驱动未自动转换。正确连接字符串写在util/DBUtil.java的静态常量里public class DBUtil { // ✅ 经过生产验证的连接字符串MySQL 8.0 private static final String URL jdbc:mysql://localhost:3306/finance_db? useSSLfalse // 关键禁用 SSL serverTimezoneAsia/Shanghai // 关键服务端时区 allowPublicKeyRetrievaltrue // 关键允许公钥检索MySQL 8.0.23 必须 characterEncodingutf8mb4 // 关键支持 emoji zeroDateTimeBehaviorCONVERT_TO_NULL // 避免 0000-00-00 日期报错 tinyInt1isBitfalse; // 兼容 tinyint(1) 当作整数而非布尔 private static final String USER root; private static final String PASSWORD your_secure_password; public static Connection getConnection() throws SQLException { return DriverManager.getConnection(URL, USER, PASSWORD); } }useSSLfalse本地开发必须关 SSL否则连接超时serverTimezoneAsia/Shanghai强制 JDBC 驱动按东八区解析时间避免2024-05-20存成2024-05-19allowPublicKeyRetrievaltrueMySQL 8.0.23 默认关闭公钥检索此参数允许驱动从服务端获取 RSA 公钥解密密码characterEncodingutf8mb4比utf8更彻底支持 4 字节 Unicode微信昵称、颜文字zeroDateTimeBehaviorCONVERT_TO_NULL当数据库有0000-00-00日期时不抛异常转为 null。注意PASSWORD绝不能硬编码在代码里实际项目应从config.properties读取见 4.3 节。此处仅为演示连接逻辑。3.3 DAO 层实现手写 JDBC 模板拒绝 Hibernate/JPA 的学习成本Swing 桌面应用的 DAO核心诉求是简单、可控、易调试、无反射黑盒。Hibernate 的Entity注解、Session 管理、Lazy Loading 陷阱在单机场景全是负优化。以IncomeDao.java为例ExpenseDao.java结构几乎一致仅表名和字段微调package dao; import model.IncomeRecord; import util.DBUtil; import java.sql.*; import java.time.LocalDate; import java.util.ArrayList; import java.util.List; public class IncomeDao { // 插入一条收入记录 public boolean insert(IncomeRecord record) { String sql INSERT INTO incomes (amount, category_id, account_id, date, note) VALUES (?, ?, ?, ?, ?); try (Connection conn DBUtil.getConnection(); PreparedStatement ps conn.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS)) { ps.setBigDecimal(1, record.getAmount()); // BigDecimal 精确存金额 ps.setInt(2, record.getCategoryId()); ps.setInt(3, record.getAccountId()); ps.setDate(4, Date.valueOf(record.getDate())); // LocalDate → java.sql.Date ps.setString(5, record.getNote()); int affected ps.executeUpdate(); if (affected 1) { // 获取自增主键更新 record.id供后续 UI 刷新用 try (ResultSet rs ps.getGeneratedKeys()) { if (rs.next()) { record.setId(rs.getInt(1)); } } return true; } } catch (SQLException e) { e.printStackTrace(); // 开发期保留上线后应转 log } return false; } // 查询某月所有收入用于月度汇总 public ListIncomeRecord findByMonth(int year, int month) { String sql SELECT id, amount, category_id, account_id, date, note FROM incomes WHERE YEAR(date) ? AND MONTH(date) ? ORDER BY date DESC; ListIncomeRecord list new ArrayList(); try (Connection conn DBUtil.getConnection(); PreparedStatement ps conn.prepareStatement(sql)) { ps.setInt(1, year); ps.setInt(2, month); try (ResultSet rs ps.executeQuery()) { while (rs.next()) { IncomeRecord r new IncomeRecord(); r.setId(rs.getInt(id)); r.setAmount(rs.getBigDecimal(amount)); r.setCategoryId(rs.getInt(category_id)); r.setAccountId(rs.getInt(account_id)); r.setDate(rs.getDate(date).toLocalDate()); // java.sql.Date → LocalDate r.setNote(rs.getString(note)); list.add(r); } } } catch (SQLException e) { e.printStackTrace(); } return list; } // 按分类统计本月收入总额用于饼图数据 public ListObject[] sumByCategoryThisMonth() { String sql SELECT c.name, SUM(i.amount) as total FROM incomes i JOIN categories c ON i.category_id c.id WHERE YEAR(i.date) YEAR(CURDATE()) AND MONTH(i.date) MONTH(CURDATE()) GROUP BY c.name ORDER BY total DESC; ListObject[] result new ArrayList(); try (Connection conn DBUtil.getConnection(); Statement stmt conn.createStatement(); ResultSet rs stmt.executeQuery(sql)) { while (rs.next()) { result.add(new Object[]{rs.getString(1), rs.getBigDecimal(2)}); } } catch (SQLException e) { e.printStackTrace(); } return result; } }insert()方法返回boolean而非int语义更清晰成功 true失败 falsefindByMonth()用YEAR()/MONTH()函数而非BETWEEN 2024-05-01 AND 2024-05-31避免月末天数差异2月只有28天sumByCategoryThisMonth()返回ListObject[]而非封装新类因为 Swing TableModel 或 Chart 数据源直接消费这种结构所有PreparedStatement都用try-with-resources确保 Connection/Statement/ResultSet 自动关闭这是内存泄漏的头号杀手。4. Swing 界面开发从 MainFrame 到 TableModel拒绝“拖拽生成器陷阱”4.1 MainFrame 设计BorderLayout 为骨架JTabbedPane 承载功能模块Swing 界面最怕“一锅炖”所有按钮、表格、输入框全塞在一个 JPanel 里用null布局硬设坐标。一旦窗口缩放、字体变大界面立刻崩坏。必须用 LayoutManager而BorderLayout是桌面应用主窗体的黄金骨架。ui/MainFrame.java核心结构package ui; import javax.swing.*; import java.awt.*; public class MainFrame extends JFrame { private JTabbedPane tabbedPane; public MainFrame() { setTitle(个人财务收支管理系统 v1.0); setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); setMinimumSize(new Dimension(1024, 768)); // 设定最小尺寸防缩太小 setLocationRelativeTo(null); // 居中显示 // 顶层容器用 BorderLayout setLayout(new BorderLayout()); // 顶部菜单栏 JMenuBar menuBar createMenuBar(); setJMenuBar(menuBar); // 中间主区域用 JTabbedPane 承载不同功能页 tabbedPane new JTabbedPane(); tabbedPane.addTab(首页概览, new OverviewPanel()); tabbedPane.addTab(收入管理, new IncomePanel()); tabbedPane.addTab(支出管理, new ExpensePanel()); tabbedPane.addTab(分类设置, new CategoryPanel()); tabbedPane.addTab(账户管理, new AccountPanel()); tabbedPane.addTab(报表分析, new ReportPanel()); add(tabbedPane, BorderLayout.CENTER); // 底部状态栏 JLabel statusLabel new JLabel(就绪, JLabel.LEFT); statusLabel.setBorder(BorderFactory.createLoweredBevelBorder()); add(statusLabel, BorderLayout.SOUTH); pack(); // 自动计算尺寸 setVisible(true); } private JMenuBar createMenuBar() { JMenuBar menuBar new JMenuBar(); JMenu fileMenu new JMenu(文件); JMenuItem exitItem new JMenuItem(退出); exitItem.addActionListener(e - System.exit(0)); fileMenu.add(exitItem); menuBar.add(fileMenu); JMenu helpMenu new JMenu(帮助); JMenuItem aboutItem new JMenuItem(关于); aboutItem.addActionListener(e - JOptionPane.showMessageDialog(this, 个人财务系统 v1.0\n作者一线工程师)); helpMenu.add(aboutItem); menuBar.add(helpMenu); return menuBar; } // 提供外部刷新 tab 内容的方法如新增收入后通知 OverviewPanel 刷新 public void refreshTab(String tabName) { for (int i 0; i tabbedPane.getTabCount(); i) { if (tabbedPane.getTitleAt(i).equals(tabName)) { Component comp tabbedPane.getComponentAt(i); if (comp instanceof Refreshable) { ((Refreshable) comp).refresh(); } break; } } } }JTabbedPane是模块化关键每个 Tab 对应一个独立 Panel职责单一便于团队分工或后期替换pack()比setSize()更可靠它根据组件内容自动计算最佳尺寸避免手动算宽高setLocationRelativeTo(null)让窗口居中比setLocation(300, 200)更适配不同分辨率Refreshable是自定义接口定义在ui/包下所有 Panel 实现它提供refresh()方法供MainFrame.refreshTab()调用——这是 Swing 里最轻量的“事件通知”机制。4.2 自定义 TableModel让 JTable 显示收支明细支持排序、编辑、实时更新SwingJTable默认DefaultTableModel无法绑定数据库实体且不支持列类型感知金额列应右对齐、日期列应格式化。必须继承AbstractTableModel手写。以IncomeTableModel.java为例ExpenseTableModel同理package ui; import model.IncomeRecord; import dao.IncomeDao; import javax.swing.table.AbstractTableModel; import java.math.BigDecimal; import java.time.format.DateTimeFormatter; import java.util.List; public class IncomeTableModel extends AbstractTableModel { private static final String[] COLUMN_NAMES {ID, 金额, 分类, 账户, 日期, 备注}; private static final Class?[] COLUMN_CLASSES { Integer.class, BigDecimal.class, String.class, String.class, String.class, String.class }; private ListIncomeRecord data; private final IncomeDao incomeDao; public IncomeTableModel() { this.incomeDao new IncomeDao(); loadData(); // 初始化加载本月数据 } Override public int getRowCount() { return data null ? 0 : data.size(); } Override public int getColumnCount() { return COLUMN_NAMES.length; } Override public String getColumnName(int columnIndex) { return COLUMN_NAMES[columnIndex]; } Override public Class? getColumnClass(int columnIndex) { return COLUMN_CLASSES[columnIndex]; } Override public Object getValueAt(int rowIndex, int columnIndex) { IncomeRecord r data.get(rowIndex); switch (columnIndex) { case 0: return r.getId(); case 1: return r.getAmount(); case 2: return getCategoryName(r.getCategoryId()); // 查库或缓存获取分类名 case 3: return getAccountName(r.getAccountId()); // 同上 case 4: return r.getDate().format(DateTimeFormatter.ofPattern(yyyy-MM-dd)); case 5: return r.getNote(); default: return ; } } // 支持编辑双击单元格修改备注 Override public boolean isCellEditable(int rowIndex, int columnIndex) { return columnIndex 5; // 只允许编辑“备注”列 } Override public void setValueAt(Object value, int rowIndex, int columnIndex) { if (columnIndex 5 value instanceof String) { IncomeRecord r data.get(rowIndex); r.setNote((String) value); // 同步更新数据库 // TODO: 调用 incomeDao.updateNote(r.getId(), (String)value); } } // 刷新数据供 IncomePanel 调用 public void loadData() { // 这里简化为查本月实际可传入年月参数 data incomeDao.findByMonth(java.time.Year.now().getValue(), java.time.Month.now().getValue()); fireTableDataChanged(); // 通知 JTable 重绘 } // 辅助方法根据 ID 查分类名实际应缓存避免每次查库 private String getCategoryName(int categoryId) { // 此处应查 categories 表为简洁省略 DAO 调用 return 工资; // 示例 } private String getAccountName(int accountId) { return 招商银行卡; // 示例 } }getColumnClass()返回具体类型让 JTable 自动应用渲染器BigDecimal→ 右对齐 千分位String→ 左对齐isCellEditable()控制哪些列可编辑避免金额、日期被误改fireTableDataChanged()是关键它触发JTable重绘比repaint()更精准getValueAt()中的getCategoryName()和getAccountName()必须加缓存如MapInteger, String否则每行渲染都查一次库100 条记录就是 200 次查询——这是新手最常踩的性能坑。4.3 配置文件驱动用 config.properties 替代硬编码支持多环境切换密码、数据库地址、备份路径这些敏感或易变参数绝不能写死在 Java 代码里。config.properties是最轻量的解决方案。在项目根目录新建config.properties# 数据库配置 db.urljdbc:mysql://localhost:3306/finance_db?useSSLfalseserverTimezoneAsia/ShanghaiallowPublicKeyRetrievaltrue db.userroot db.passwordyour_real_password_here # 备份配置 backup.pathC:/finance_backup/ backup.retention.days30 # 界面配置 ui.font.size12 ui.themesystemutil/ConfigLoader.java加载逻辑package util; import java.io.IOException; import java.io.InputStream; import java.util.Properties; public class ConfigLoader { private static final Properties props new Properties(); static { try (InputStream is ConfigLoader.class.getClassLoader() .getResourceAsStream(config.properties)) { if (is ! null) { props.load(is); } else { throw new RuntimeException(❌ config.properties 未找到请检查是否放在 classpath 根目录); } } catch (IOException e) { throw new RuntimeException(❌ 加载 config.properties 失败, e); } } public static String getProperty(String key) { return props.getProperty(key); } public static String getProperty(String key, String defaultValue) { return props.getProperty(key, defaultValue); } }getResourceAsStream(config.properties)要求文件在src/下编译后自动复制到bin/而非项目根目录static { }块保证类加载时就初始化避免每次调用都 IOgetProperty(key, defaultValue)提供兜底值防止配置缺失导致空指针。在DBUtil.java中调用private static final String URL ConfigLoader.getProperty(db.url); private static final String USER ConfigLoader.getProperty(db.user); private static final String PASSWORD ConfigLoader.getProperty(db.password);提示.gitignore中务必加入config.properties防止密码泄露生产环境部署时用脚本替换该文件内容。5. 避坑指南那些让 Eclipse Swing MySQL 组合在第 3 小时就崩溃的 5 个真实问题5.1 现象Eclipse 运行 MainApp.java 报错 “Error: Could not find or load main class main.MainApp”原因MainApp.java不在main包下或包声明写错如package main;缺少分号src/没被 Eclipse 识别为 source folder右键项目 →Properties → Java Build Path → Source确认src在列表中且Excluded为空类名与文件名不一致MainApp.java里写了public class MyAppmain方法签名错误如public static void main(string[] args)少了大写String。解决检查MainApp.java第一行是否为package main;右键项目 →Refresh再右键src→Build Path → Use as Source Folder确认文件名 类名复制标准 main 方法模板public static void main(String[] args) { ... }。5.2 现象Swing 界面中文显示为方块□□□原因Eclipse 控制台编码不是 UTF-8影响 System.out 输出但不导致界面乱码真正原因Swing 组件未设置字体而 Windows 系统默认字体如 SimSun不支持某些 Unicode 字符或 JDK 字体渲染引擎未启用 LCD 渲染。解决在MainFrame构造函数开头setTitle()之前添加// 全局设置 Swing 字体Windows 推荐微软雅黑macOS 用 PingFang SC UIManager.put(Label.font, new Font(Microsoft YaHei, Font.PLAIN, 12)); UIManager.put(Button.font, new Font(Microsoft YaHei, Font.PLAIN, 12)); UIManager.put(TextField.font, new Font(Microsoft YaHei, Font.PLAIN, 12)); UIManager.put(Table.font, new Font(Microsoft YaHei, Font.PLAIN, 12)); // 启用 LCD 渲染提升中文清晰度 System.setProperty(awt.useSystemAAFontSettings, on); System.setProperty(swing.aatext, true);5.3 现象MySQL 连接成功但执行INSERT后数据不入库重启程序又消失原因事务未提交JDBC 默认开启事务executeUpdate()后未调用conn.commit()Connection被try-with-resources自动关闭但commit()在close()之后调用语法错误使用了autoCommitfalse但忘记commit()。解决所有涉及写操作的 DAO 方法必须显式 commitpublic boolean insert(IncomeRecord record) { String sql ...; try (Connection conn DBUtil.getConnection(); PreparedStatement ps conn.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS)) { // ... 设置参数 ... int affected ps.executeUpdate(); conn.commit(); // ✅ 必须在此处 commit return affected 1; } catch (SQLException e) { try { if (conn ! null) conn.rollback(); // ✅ 出错回滚 } catch (SQLException ex) { ex.printStackTrace(); } e.printStackTrace(); return false; } }5.4 现象JTable 点击排序后数据错乱或崩溃原因TableRowSorter与自定义TableModel不兼容AbstractTableModel未实现getRowCount()等方法的线程安全而排序器会在后台线程调用fireTableDataChanged()被在非 EDTEvent Dispatch Thread线程调用Swing 线程不安全。解决排序必须在 EDT 中触发SwingUtilities.invokeLater(() - { TableRowSorterIncomeTableModel sorter new TableRowSorter(model); table.setRowSorter(sorter); });本文还有配套的精品资源点击获取
RELATED

相关推荐

Java小游戏毕设避坑指南:面向对象建模实战

Java小游戏毕设避坑指南:面向对象建模实战

简介:本资源是面向计算机及相关专业本科生(大一至大四)的Java面向对象程序设计课程项目实践包,聚焦小游戏开发这一典型OOP应用场景,帮助学生通过完整项目掌握封装、继承、多态等核心概念,并完成从需求分析、…

📅 2026/10/30 17:09:33
C++ Qt扫雷项目实战:从环境搭建到高分课程设计

C++ Qt扫雷项目实战:从环境搭建到高分课程设计

简介:本资源是一份高分C Qt课程设计项目——扫雷小游戏的完整实现,面向计算机类专业(如计科、人工智能、通信工程等)在校学生、教师及初学者,适用于课程设计、毕业设计、课设作业或项目立项演示。压缩包共44个文件&…

📅 2026/10/30 17:09:33
.NET代码加固选型指南:商业混淆与开源方案对比及自测避坑

.NET代码加固选型指南:商业混淆与开源方案对比及自测避坑

简介:围绕 DotNETReactor 代码保护工具整理的参考包,面向希望了解 .NET 程序集混淆、代码保护与项目部署流程的 C# 开发者、安全研究人员及中高级技术人员,可用于对照示例工程理解工具结构与保护思路。压缩包共 105 个文件,约 3.6…

📅 2026/10/30 17:09:33
MORE NEWS

更多资讯

📰

extract-xiso完全指南:Xbox光盘镜像提取与打包实战

简介:extract-xiso 是一款开源的光盘映像备份工具,专注于创建和提取 Xbox 游戏光盘的 .iso 文件,适用于 Linux、FreeBSD、macOS、Windows 等多个操作系统,目标用户是游戏备份爱好者、模拟器玩家及需要处理 Xbox 镜像的开发者。这份…

📰

地铁BAS PLC为何必须用STEP7 V5.5?兼容性与现场准入硬约束

简介:本资源是一套完整的地铁环境监控系统(BAS)PLC控制程序工程包,面向自动化、轨道交通及工业控制领域的工程师、高校师生与PLC初学者,聚焦西门子S7系列PLC在真实地铁场景中的工程化应用。资源基于STEP7 V5.5开发&…

📰

C语言+easyX实现Flappy Bird:图形编程底层实战指南

简介:这是一份面向C语言初学者与图形编程入门者的Flappy Bird像素鸟游戏实战源码包,聚焦Windows平台下基于easyX图形库的游戏开发实践,帮助学习者掌握双缓冲绘图、物理模拟(重力/碰撞)、状态机管理及音效集成等核心技能…

📰

【纯白嫖】IEC60870-5 中文版下载指南:101/104 协议标准一次拿齐全集

【纯白嫖】IEC60870-5 中文版下载指南:101/104 协议标准一次拿齐全集 【免费下载链接】IEC60870-5全集中文版本下载 IEC60870-5全集中文版本下载 项目地址: https://gitcode.com/open-source-toolkit/fc96f IEC60870-5 原版标准难找难读。IEC60870-5全集中文…

📰

刚更新的首次启动流程:模型来源、系统授权、Agent 配置一次配完

刚更新的首次启动流程:模型来源、系统授权、Agent 配置一次配完 【免费下载链接】OpenBot Open-source AI coworkers that each get a computer of their own: a browser, files and tools, with every action decided before it happens and recorded after. Bring…

📰

【资源自取】倪海厦软件完整下载指南:人纪、天纪、医案查询系统三步跑通

【资源自取】倪海厦软件完整下载指南:人纪、天纪、医案查询系统三步跑通 【免费下载链接】倪海厦三套软件资源分享 倪海厦三套软件资源分享本仓库提供倪海厦先生的三套电脑软件资源,经过测试,确保可用性 项目地址: https://gitcode.com/ope…

TODAY

今日更新

THIS WEEK

本周精选

THIS MONTH

本月热门

读完文章,想聊聊您的网站?

告诉我们您的行业与需求,资深顾问一对一梳理方案与报价,全程免费。

📞 💬