news 2026/6/11 1:58:01

Java Swing写的本地超市系统:顾客购物+管理员管货,全用文本文件存数据

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Java Swing写的本地超市系统:顾客购物+管理员管货,全用文本文件存数据

本文还有配套的精品资源,点击获取

简介:一个纯Java Swing开发的离线超市管理桌面程序,不用数据库也不连网络,所有数据都存在本地文本文件里。顾客能看商品、加购、付款;管理员能增删改查商品信息(名称、价格、库存)和用户账号。登录后自动跳转对应界面,顾客看到购物车页面,管理员进入后台管理页,不同角色背景图也不同(比如mainBackground.jpg、adminPage_Background.jpg)。项目自带完整源码,包括主入口Main.java和多个测试类(Test01.java到Test03.java),还有专门存放图片的backgroundPics和otherPics文件夹,用户信息存usersInfo.txt,商品数据在wares_data目录下。配置文件EX_2.0_jar.xml用于打包,workspace.xml记录开发环境设置,out和artifacts是编译输出结果。整个结构适合Java新手练手,重点覆盖GUI组件布局、按钮事件响应、文件读写(IO)、多窗口切换和简单权限分流逻辑。

1. 项目概述:为什么一个“不用数据库”的超市系统反而更值得学?

你可能刚学完Java基础,正琢磨着做点什么练手——写个计算器太简单,写个学生管理系统又绕不开MySQL配置、JDBC驱动、SQL语法这些“拦路虎”。这时候,一个纯用Java Swing写的本地超市系统,所有数据就存几个.txt文件里,连网络都不用连,反而成了最扎实的进阶跳板。它不是“简陋”,而是把核心逻辑从数据库抽象层里彻底剥出来,让你亲手摸到GUI事件流怎么触发、数据怎么落地成字节、权限怎么靠对象状态控制、页面怎么在内存里干净切换——这些才是桌面应用真正的骨架。

我带过不少刚毕业的学生做这个项目,发现他们最大的误区是:一上来就猛敲JFrameJButton,结果界面堆满了却不知道按钮点击后数据到底去了哪。而这个系统恰恰反其道而行之:它用最朴素的方式告诉你——一个按钮的本质,就是一次方法调用;一次保存,就是把对象转成字符串再写进文件;角色切换,不过是根据登录返回的对象类型,决定new哪一个JFrame而已。没有ORM映射,没有连接池管理,没有事务回滚,只有你和BufferedWriterObjectInputStreamActionListener面对面较劲。usersInfo.txt里明文存着用户名密码(当然实际项目要加盐加密,但初学阶段先理解流程),wares_data目录下每个商品一行文本,格式固定如苹果|5.8|120|水果,你甚至能直接用记事本打开修改,改完重启程序立刻生效。这种“所见即所得”的反馈,对建立编程直觉太关键了。它不教你如何造火箭,但确保你亲手拧紧每一颗螺丝——Swing布局管理器怎么让组件自适应窗口大小,FileReader读取时中文乱码怎么用InputStreamReader指定UTF-8编码,JTable表格模型怎么动态刷新数据,管理员删除商品后购物车里的同名商品要不要自动失效……这些问题的答案,全藏在那几行看似简单的IO代码和事件监听器里。所以别小看这个“土味”系统,它是一面镜子,照出你对Java核心机制的理解深度:是不是真懂对象生命周期?是不是清楚IO流的关闭顺序?是不是明白Swing线程安全的边界在哪?当你能把这些细节抠明白,再上手Spring Boot或JavaFX,才会真正游刃有余。

2. 整体架构与设计思路:没有数据库,怎么让数据“活”起来?

这个系统的精妙之处,在于它用极简的文本存储,模拟出了数据库的核心能力:持久化、查询、增删改查(CRUD)和关系映射。它没用任何框架,全靠Java原生API和合理的面向对象设计撑起整个数据层。我们来拆解它的三层结构:表现层(Swing GUI)、业务逻辑层(Service类)、数据访问层(DAO类)。重点在于,DAO层不对接数据库,而是对接文件系统——这才是它区别于其他教程项目的灵魂所在。

2.1 数据模型设计:用POJO承载业务语义

系统定义了两个核心实体类:UserWareUser类包含usernamepasswordrole(”admin”或”customer”)三个字段,Ware类则封装namepricestockcategory。注意,这里没有用int存价格,而是double——因为超市商品价格常有小数(如9.9元),用整数分单位虽精确但计算繁琐,初学者用double更直观,后续可升级为BigDecimal。每个类都重写了toString()方法,这是文件存储的关键:Ware.toString()返回"苹果|5.8|120|水果"User.toString()返回"zhangsan|123456|customer"。这种竖线分隔的格式,简单、易解析、抗干扰强(比CSV少处理引号和逗号)。更重要的是,它让对象与文本之间形成了一对一的映射关系——序列化不是目的,可读性才是调试的生命线。你打开usersInfo.txt,一眼就能看出哪个用户是管理员,库存数字是否异常,这比二进制序列化或JSON格式对新手友好太多。

2.2 文件存储策略:目录即“数据库”,文件即“表”

系统将文件组织成微型数据库:
-usersInfo/目录是“用户库”,其中usersInfo.txt是“用户表”,每行一条用户记录;
-wares_data/目录是“商品库”,其中wares.txt是“商品主表”,每行一条商品记录;
-allUsers/目录看似冗余,实则是为未来扩展预留的“用户快照”区,比如记录用户历史订单(当前版本未实现,但目录结构已预留)。

这种设计规避了单文件臃肿问题。想象一下,如果所有商品都塞进一个超大wares.txt,每次增删都要读取全部内容再重写,效率极低。而按目录划分,wares_data/下可以分门别类建子目录(如wares_data/fruits/wares_data/dairy/),虽然当前版本没用上,但结构已为扩展埋好伏笔。文件读写采用典型的“读-改-写”模式:以WareDAO为例,loadAllWares()方法先用Files.readAllLines(Paths.get("wares_data/wares.txt"))一次性读入所有行,再逐行split("\\|")解析成Ware对象并加入ArrayListsaveWares(List<Ware> wares)则遍历列表,对每个ware.toString()追加换行符后,用Files.write()覆盖原文件。这里有个关键细节:必须用StandardOpenOption.CREATE, StandardOpenOption.WRITE, StandardOpenOption.TRUNCATE_EXISTING三个选项,确保文件被清空重写,避免旧数据残留。我见过太多初学者只写StandardOpenOption.WRITE,结果新数据追加在旧数据后面,导致程序读取时解析错乱。

2.3 权限控制逻辑:身份即状态,状态驱动视图

权限不是靠复杂的RBAC模型,而是最朴素的状态机。登录验证在LoginPanel中完成:用户输入账号密码后,UserDAO.authenticate(username, password)方法会遍历usersInfo.txt,逐行比对。一旦匹配成功,它不返回布尔值,而是直接返回一个完整的User对象,其中role字段已明确标识身份。这个对象被传递给主程序Main.java,后者根据user.getRole()的值,决定实例化CustomerMainFrame还是AdminMainFrame。这就是权限分流的本质——没有中间件拦截,没有注解扫描,只有一个if-else判断,却精准控制了整个应用的入口和行为边界。顾客界面禁用所有商品管理按钮,管理员界面则隐藏购物车结算功能。这种设计教会初学者一个真理:权限控制的起点,永远是用户认证后获得的那个“身份对象”,后续所有功能开关,都是对这个对象属性的条件判断。

3. 核心模块详解:从登录到结账,每一步都在教你怎么写健壮的Swing代码

这个系统的价值,不在它有多炫酷,而在它把Swing开发中最容易踩坑的环节,都用最直白的方式暴露出来。下面我带你走一遍从输入账号到完成支付的完整链路,重点讲透那些教科书里不会写、但实际开发天天遇到的细节。

3.1 登录模块:不只是验证,更是状态初始化的起点

登录界面LoginPanel是一个JPanel,里面放了JTextField(账号)、JPasswordField(密码)、JButton(登录)。关键不在布局,而在事件处理:

loginButton.addActionListener(e -> { String username = usernameField.getText().trim(); String password = new String(passwordField.getPassword()); // 注意!getPassword()返回char[],必须转String if (username.isEmpty() || password.isEmpty()) { JOptionPane.showMessageDialog(this, "账号或密码不能为空!", "输入错误", JOptionPane.WARNING_MESSAGE); return; } User user = UserDAO.authenticate(username, password); if (user != null) { // 认证成功,销毁登录面板,启动主框架 SwingUtilities.invokeLater(() -> { JFrame mainFrame = null; if ("admin".equals(user.getRole())) { mainFrame = new AdminMainFrame(user); } else { mainFrame = new CustomerMainFrame(user); } mainFrame.setVisible(true); ((JFrame) SwingUtilities.getWindowAncestor(this)).dispose(); // 安全关闭登录窗口 }); } else { JOptionPane.showMessageDialog(this, "账号或密码错误!", "登录失败", JOptionPane.ERROR_MESSAGE); } });

这段代码藏着三个硬核知识点:
1.密码安全处理JPasswordField.getPassword()返回char[]而非String,这是Java为防止密码被字符串常量池缓存而做的安全设计。必须用new String(char[])转换,且转换后应立即Arrays.fill(char[], '\0')清空数组(当前项目未做,但这是生产环境铁律)。
2.Swing线程安全SwingUtilities.invokeLater()确保GUI更新在事件调度线程(EDT)中执行。如果直接new AdminMainFrame()并在当前线程setVisible(true),可能导致界面卡死或组件渲染异常——这是Swing新手第一大坑。
3.窗口生命周期管理((JFrame) SwingUtilities.getWindowAncestor(this)).dispose()是安全关闭登录窗口的正确姿势。它通过this(即LoginPanel)向上查找最近的JFrame父容器,然后调用dispose()释放资源。绝不能用System.exit(0),那会粗暴杀死整个JVM。

3.2 顾客购物模块:购物车不是集合,而是状态同步的纽带

顾客主界面CustomerMainFrame的核心是JTable展示商品列表和JList显示购物车。难点在于:当用户双击商品列表某行,如何将该商品添加到购物车,并实时更新JList?这里涉及Swing的MVC思想。

购物车数据由Cart类管理,它内部持有一个List<Ware>Cart不是简单容器,而是具备业务逻辑的实体:
-add(Ware ware):检查库存,若ware.getStock() <= 0则弹窗提示“缺货”,否则添加;
-remove(Ware ware):从列表移除,但不改变商品原始库存;
-calculateTotal():遍历购物车,累加ware.getPrice() * quantity(注意,当前版本购物车未记录数量,每次添加即+1,简化版)。

JTable的模型是DefaultTableModel,但它的数据源来自WareDAO.loadAllWares()。关键同步点在双击事件:

waresTable.addMouseListener(new MouseAdapter() { @Override public void mouseClicked(MouseEvent e) { if (e.getClickCount() == 2) { // 双击 int row = waresTable.getSelectedRow(); if (row >= 0) { // 将视图行号转为模型行号(处理排序过滤时很重要) int modelRow = waresTable.convertRowIndexToModel(row); Ware selectedWare = allWares.get(modelRow); // allWares是loadAllWares()返回的列表 if (Cart.getInstance().add(selectedWare)) { // 添加成功,刷新购物车JList cartListModel.addElement(selectedWare.getName() + " x1"); // 简化显示 totalLabel.setText("总计:" + Cart.getInstance().calculateTotal() + " 元"); } } } } });

这里convertRowIndexToModel()是精髓。JTable支持排序,用户点击列头会重排行序,此时getSelectedRow()返回的是视图行号,而allWares列表索引是模型行号,不转换就会选错商品。这个细节,90%的初学者教程都会忽略,导致排序后双击总是添加第一个商品。

3.3 管理员后台模块:增删改查背后的文件原子性保障

管理员界面AdminMainFrame提供四个核心按钮:添加商品、修改商品、删除商品、刷新列表。所有操作都直击文件IO痛点。

以“删除商品”为例,deleteWare()方法不能简单地从allWares列表里remove(),因为那只是内存操作,文件没变。它必须:
1. 从allWares中找到目标商品并移除;
2. 调用WareDAO.saveWares(allWares)重写整个wares.txt
3. 刷新JTable模型,调用tableModel.setRowCount(0)清空,再遍历allWares逐行addRow()

但这里有个致命风险:如果saveWares()执行到一半(如磁盘满),文件被截断,数据就永久丢失了。生产环境必须用原子写入。当前项目虽未实现,但作为进阶提示,我告诉你标准做法:先将新数据写入临时文件wares.txt.tmp,写入成功后,用Files.move(Paths.get("wares.txt.tmp"), Paths.get("wares.txt"), StandardCopyOption.REPLACE_EXISTING)原子替换。move操作在绝大多数文件系统上是原子的,要么全成功,要么全失败,绝不会出现半截文件。

另一个易错点是“修改商品”。用户在JTable上双击某行进入编辑,修改后点“保存”,此时不能直接waresTable.setValueAt(newName, row, 0),因为JTable的模型是DefaultTableModel,它只管显示,不管业务逻辑。正确流程是:获取用户修改后的值 → 找到allWares中对应的商品对象 → 修改其name字段 → 调用saveWares(allWares)→ 刷新表格。否则,内存对象和文件数据会严重不一致。

4. 实操过程与关键配置:从零开始搭建、运行、调试的完整路径

现在,我们把理论落到键盘上。假设你刚下载完项目压缩包,面对一堆文件夹有点懵——别急,按这个顺序操作,10分钟内就能跑起来,并理解每个步骤的意义。

4.1 环境准备与项目导入:IntelliJ IDEA下的标准流程

首先确认你的开发环境:JDK 8或11(Swing在JDK 11+仍完全兼容,无需模块化配置)。打开IntelliJ IDEA,选择Open,定位到解压后的项目根目录(即包含.gitignoresrcbackgroundPics的文件夹)。IDEA会自动识别为Maven或Gradle项目吗?不会。因为这是一个纯Java项目,没有pom.xmlbuild.gradle。所以你要手动配置:

  1. Project Structure(Ctrl+Alt+Shift+S)中,Project SettingsProjectProject SDK选择你安装的JDK;
  2. ModulesSources标签页,将src文件夹标记为Sources(蓝色图标),resources(如果有)标记为Resources
  3. Artifacts标签页,点击+JARFrom modules with dependencies,选择Main类作为Main Class,勾选Include in project build。这一步对应项目中的EX_2.0_jar.xml,它定义了打包规则:把所有class文件和backgroundPics等资源打进一个jar包。

提示:workspace.xml是IDEA的私有配置文件,记录了你的窗口布局、断点等,不必关注,也绝不应提交到Git。

4.2 运行前的必要初始化:创建缺失的文件与目录

项目首次运行必报错,因为usersInfo.txtwares.txt默认不存在。这是故意为之的设计,逼你理解“文件不存在”这个常见异常。你需要手动创建:

  1. 在项目根目录下,新建文件夹usersInfo
  2. usersInfo内,新建空文件usersInfo.txt
  3. 新建文件夹wares_data
  4. wares_data内,新建空文件wares.txt

然后,往usersInfo.txt里写一行测试数据:admin|admin123|admin(管理员账号);往wares.txt里写几行商品:大米|5.5|50|粮油鸡蛋|6.8|200|生鲜。保存后,运行Main.java,输入admin/admin123即可登录管理员界面。这个手动创建过程,比任何教程都深刻地教会你:程序依赖的外部资源(文件、目录),必须在运行前由运维或安装脚本准备好,代码里要用Files.createDirectories()主动创建目录,用Files.exists()检查文件是否存在

4.3 关键资源加载:背景图路径与编码的生死线

界面美观靠backgroundPics里的三张jpg图:mainBackground.jpg(登录页)、adminPage_Background.jpg(管理员页)、cusPage_Background.jpg(顾客页)。加载代码类似:

ImageIcon icon = new ImageIcon("backgroundPics/mainBackground.jpg"); JLabel backgroundLabel = new JLabel(icon);

但这里埋着一个巨坑:路径是相对路径,相对于java -cp指定的classpath根目录。如果你在IDEA里直接运行Main.java,classpath根是项目根目录,backgroundPics/能找到;但如果你打包成jar运行,backgroundPics/必须在jar包内部。因此,正确的加载方式是:

URL imageUrl = getClass().getClassLoader().getResource("backgroundPics/mainBackground.jpg"); if (imageUrl != null) { ImageIcon icon = new ImageIcon(imageUrl); } else { // 回退到绝对路径或默认颜色 }

getClass().getClassLoader().getResource()会从classpath中搜索,无论是在IDEA调试还是jar包运行,都能准确定位。另外,图片文件本身必须是UTF-8无BOM编码保存,否则某些Windows系统会因路径含中文而加载失败(虽然本项目图片名是英文,但养成习惯很重要)。

4.4 源码结构解读:从Main.java到Test类,谁在驱动一切?

项目源码看似散乱,实则层次分明:
-Main.java:程序唯一入口,负责创建JFrame,设置LoginPanel为内容面板,启动事件循环;
-pages/目录:存放所有GUI面板类,LoginPanelCustomerMainFrameAdminMainFrame都在此,遵循“一个页面一个类”原则;
-src/目录:核心业务类,UserDAOWareDAOCartUserWare,纯粹的数据操作,与GUI解耦;
-Test01.javaTest03.java:这不是单元测试,而是教学演示脚本Test01.java专门演示文件读写:WareDAO.loadAllWares()saveWares()Test02.java演示Swing事件:一个按钮点击弹窗;Test03.java演示多窗口切换。它们是独立的main方法,你可以逐个运行,观察控制台输出,理解底层逻辑后再看GUI代码,事半功倍。

注意:out/artifacts/是IDEA编译输出目录,out/production/下是class文件,artifacts/下是打包好的jar。不要手动修改这些目录里的文件,它们由IDEA自动生成。

5. 常见问题与排查技巧实录:那些让我熬夜到凌晨三点的Bug

这个项目就像一面镜子,照出你对Java基础的真实掌握程度。下面这些坑,我当年一个没落下,全是在调试中用血泪总结出来的,现在毫无保留分享给你。

5.1 文件读写类问题:中文乱码、空指针、找不到文件

问题1:打开usersInfo.txt全是乱码,显示“鎵撳瘑閿欒”
-原因Files.readAllLines()默认使用系统编码(Windows是GBK),而你的文件是UTF-8保存的。
-解决:显式指定编码:Files.readAllLines(Paths.get("usersInfo/usersInfo.txt"), StandardCharsets.UTF_8)
-经验:永远不要依赖默认编码!在UserDAO.authenticate()里,读取每一行后,用line.trim()去除首尾空格和不可见字符(如BOM),再split("\\|"),否则"admin ""admin"会被视为不同用户。

问题2:UserDAO.authenticate()返回null,但文件明明有数据
-原因Files.readAllLines()读取时,如果文件最后一行没有换行符\nreadAllLines()会漏掉最后一行!
-解决:在saveWares()写入时,确保每行末尾都有换行符:writer.write(ware.toString() + "\n")
-经验:用Files.lines()替代readAllLines(),它是Stream API,能更好处理边界情况,且支持try-with-resources自动关闭。

问题3:“Exception in thread ‘AWT-EventQueue-0’ java.lang.NullPointerException”
-原因JTableDefaultTableModel被设为null,或allWares列表为null
-排查:在AdminMainFrame构造方法里,loadAllWares()后立刻加断点,检查allWares.size()是否为0。如果是,说明WareDAO.loadAllWares()抛异常被静默吞掉了。
-解决:在DAO方法里,catch (IOException e)后必须e.printStackTrace()log.error("加载商品失败", e),绝不能空catch!

5.2 Swing GUI类问题:界面卡死、组件不显示、事件不响应

问题1:点击登录按钮没反应,控制台也没输出
-原因ActionListener没正确绑定,或者loginButton是局部变量,在方法结束后被GC,监听器失效。
-排查:检查loginButton声明位置——必须是类的成员变量(private JButton loginButton;),不能在initComponents()JButton loginButton = new JButton();这样声明。
-经验:用IDEA的“Find Usages”功能,搜索loginButton.addActionListener,确认只绑定了一次。重复绑定会导致事件触发两次。

问题2:管理员界面打开后,JTable一片空白,但数据明明加载了
-原因DefaultTableModel的列名数组和数据行数不匹配。例如,你定义了{"名称","价格","库存","分类"}四列,但addRow()时只传了三个元素的数组。
-解决:在refreshTable()方法里,先model.setRowCount(0)清空,再确保addRow(new Object[]{ware.getName(), ware.getPrice(), ware.getStock(), ware.getCategory()})传入四个元素。
-经验:在JTable构造时,用new JTable(new DefaultTableModel(data, columnNames)),让模型和数据一起初始化,比后期setModel()更稳定。

问题3:切换到管理员界面后,程序假死,鼠标变成沙漏
-原因:在EDT线程里执行了耗时IO操作,如WareDAO.loadAllWares()直接在AdminMainFrame构造方法里调用,阻塞了整个GUI线程。
-解决:用SwingWorker异步加载:

SwingWorker<List<Ware>, Void> worker = new SwingWorker<>() { @Override protected List<Ware> doInBackground() throws Exception { return WareDAO.loadAllWares(); // 耗时操作放这里 } @Override protected void done() { try { allWares = get(); // 获取结果 refreshTable(); // 在EDT中更新UI } catch (Exception e) { e.printStackTrace(); } } }; worker.execute();
  • 经验:任何可能超过100ms的操作(文件读写、网络请求、复杂计算),都必须放到SwingWorkerExecutorService中执行,这是Swing响应性的生命线。

5.3 项目结构与构建类问题:打包后图片不显示、jar运行报错

问题1:用EX_2.0_jar.xml打包的jar,双击运行闪退,命令行运行显示“NoClassDefFoundError”
-原因EX_2.0_jar.xml是IDEA的artifact配置,它可能没把backgroundPics目录包含进jar包。
-解决:在Project StructureArtifacts里,展开你的jar条目,点击+Directory Content,选择backgroundPics文件夹,确保它出现在jar包的根路径下。
-验证:用jar -tf your-app.jar | grep background查看jar包内容。

问题2:jar包运行后,背景图加载为空白,但控制台无报错
-原因ImageIcon("backgroundPics/mainBackground.jpg")在jar包里找不到路径,因为jar内路径是backgroundPics/mainBackground.jpg,但代码试图在文件系统中找。
-解决:必须用getClass().getClassLoader().getResource(),如前所述。这是最常被忽视的路径陷阱。

问题3:Test01.java能正常读写文件,但Main.java运行时报AccessDeniedException
-原因Test01.java在项目根目录运行,有写权限;而Main.java被打包成jar后,可能被放在Program Files等受保护目录,Windows阻止写入。
-解决:将数据文件存放在用户主目录下,如System.getProperty("user.home") + "/supermarket/usersInfo.txt"。这是桌面应用的标准实践,避免权限问题。

6. 进阶优化与扩展建议:从“能跑”到“能用”的跃迁路径

这个项目是绝佳的起点,但绝不是终点。当你能流畅运行、修改、调试它之后,下一步就是把它从“教学Demo”打磨成“可用原型”。以下是几条经过验证的、务实的升级路径,每一步都直击真实开发痛点。

6.1 数据安全加固:告别明文密码,拥抱基础加密

当前usersInfo.txt里密码是明文,这在任何场景下都是不可接受的。升级方案不是一步到位上SHA-256,而是分两步走:

第一步:加盐哈希(Salted Hash)
- 在User类中,password字段不再存明文,而是存哈希值;
- 创建工具类PasswordUtil,包含hash(String password, String salt)verify(String inputPassword, String storedHash, String salt)
- 盐值(salt)不能写死,为每个用户生成随机盐,与哈希值一起存入文件,格式改为zhangsan|$2a$10$randomSalt$hashedPassword|customer
- 使用BCryptPasswordEncoder(需引入spring-security-crypto依赖,轻量级)或Java原生MessageDigestSHA-256+SecureRandom生成盐)。

第二步:敏感信息分离
- 将usersInfo.txt拆分为users_basic.txt(用户名、角色、盐)和users_secure.txt(哈希密码),后者设为系统隐藏文件,限制读取权限。这模拟了生产环境的“最小权限原则”。

6.2 UI体验升级:从“能用”到“顺手”的细节打磨

Swing的默认外观(Metal)早已过时。升级不求华丽,但求专业:

  • 更换Look and Feel:在Main.java开头,添加UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName()),让程序使用Windows/macOS原生主题,瞬间提升专业感;
  • 表格增强:为JTable添加TableRowSorter,支持点击列头排序;添加JScrollPane滚动条;为库存列设置红色字体警示(stock < 10);
  • 输入校验JTextField输入价格时,用DocumentFilter实时过滤非数字字符,避免用户误输字母导致NumberFormatException

6.3 功能扩展:小步快跑,验证你的架构能力

不要一上来就加“会员积分”“促销活动”,先做三个高价值小功能:

  1. 购物车持久化:当前购物车关机就丢。新增cart_data/目录,用户退出时自动保存购物车到cart_data/zhangsan_cart.txt,下次登录时恢复。这教会你“会话状态管理”的本质;
  2. 操作日志:在WareDAOsaveWares()方法里,追加写入logs/operation.log,记录“admin于2023-10-05 14:22:33 删除商品:苹果”。用SimpleDateFormat格式化时间,这是审计追踪的起点;
  3. 配置中心化:将所有硬编码路径("usersInfo/","wares_data/")提取到config.properties文件,用Properties.load()读取。这让你第一次体会到“配置与代码分离”的威力。

6.4 工程化收尾:从个人项目到可交付产品的最后一步

一个能交付的桌面应用,必须有安装包和自检能力:

  • 制作Windows安装包:用Inno Setup(免费)将jar包、backgroundPicsconfig.properties打包成.exe安装程序,自动创建桌面快捷方式,设置开机自启选项(需用户授权);
  • 自检向导:程序启动时,自动检查usersInfo/wares_data/目录是否存在,不存在则创建;检查usersInfo.txt是否可读写,不可写则弹窗提示“请以管理员身份运行”。这能消灭80%的用户咨询。

我在实际带团队时,把这个项目作为新人入职第一周的考核任务:第一天跑通,第二天修复三个指定Bug,第三天加一个新功能,第四天写一份技术文档,第五天给团队做10分钟分享。它像一把手术刀,精准切开Java桌面开发的每一层肌肉——Swing的事件模型、IO的异常处理、面向对象的设计、工程化的思维。当你亲手把一个文本文件变成支撑起整个超市运转的数据中枢时,那种“创造”的实感,远胜于背诵一百条API文档。所以,别犹豫,现在就打开IDEA,创建那个usersInfo.txt,敲下第一行admin|admin123|admin,然后按下运行。接下来的每一个NullPointerException,每一次文件读写失败,都是你和Java世界建立真实连接的印记。

本文还有配套的精品资源,点击获取

简介:一个纯Java Swing开发的离线超市管理桌面程序,不用数据库也不连网络,所有数据都存在本地文本文件里。顾客能看商品、加购、付款;管理员能增删改查商品信息(名称、价格、库存)和用户账号。登录后自动跳转对应界面,顾客看到购物车页面,管理员进入后台管理页,不同角色背景图也不同(比如mainBackground.jpg、adminPage_Background.jpg)。项目自带完整源码,包括主入口Main.java和多个测试类(Test01.java到Test03.java),还有专门存放图片的backgroundPics和otherPics文件夹,用户信息存usersInfo.txt,商品数据在wares_data目录下。配置文件EX_2.0_jar.xml用于打包,workspace.xml记录开发环境设置,out和artifacts是编译输出结果。整个结构适合Java新手练手,重点覆盖GUI组件布局、按钮事件响应、文件读写(IO)、多窗口切换和简单权限分流逻辑。


本文还有配套的精品资源,点击获取

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/6/11 1:50:53

Windows平台一键安装的C# FTP服务器,带网页管理后台和系统服务支持

本文还有配套的精品资源&#xff0c;点击获取 简介&#xff1a;这个资源包提供一个开箱即用的FTP服务器解决方案&#xff0c;用纯C#编写&#xff0c;无需额外运行时依赖&#xff0c;直接在Windows上安装运行。安装包包含MSI和EXE两种格式&#xff0c;支持静默安装、开机自启…

作者头像 李华
网站建设 2026/6/11 1:48:20

FireRedTTS2实战指南:5步构建专属多说话人对话语音模型

FireRedTTS2实战指南&#xff1a;5步构建专属多说话人对话语音模型 【免费下载链接】FireRedTTS2 Long-form streaming TTS system for multi-speaker dialogue generation 项目地址: https://gitcode.com/gh_mirrors/fi/FireRedTTS2 FireRedTTS2是一款革命性的长音频流…

作者头像 李华
网站建设 2026/6/11 1:46:35

用C51单片机+蜂鸣器复刻《起风了》完整教程(附源码与乐谱转换技巧)

用C51单片机蜂鸣器复刻《起风了》完整教程&#xff08;附源码与乐谱转换技巧&#xff09; 当蜂鸣器遇上经典旋律&#xff0c;单片机也能化身微型音乐盒。本文将手把手教你如何用C51单片机驱动蜂鸣器完整演奏《起风了》&#xff0c;从乐谱解析到代码实现&#xff0c;揭秘电子音乐…

作者头像 李华