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

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事件流怎么触发、数据怎么落地成字节、权限怎么靠对象状态控制、页面怎么在内存里干净切换——这些才是桌面应用真正的骨架。我带过不少刚毕业的学生做这个项目发现他们最大的误区是一上来就猛敲JFrame和JButton结果界面堆满了却不知道按钮点击后数据到底去了哪。而这个系统恰恰反其道而行之它用最朴素的方式告诉你——一个按钮的本质就是一次方法调用一次保存就是把对象转成字符串再写进文件角色切换不过是根据登录返回的对象类型决定new哪一个JFrame而已。没有ORM映射没有连接池管理没有事务回滚只有你和BufferedWriter、ObjectInputStream、ActionListener面对面较劲。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承载业务语义系统定义了两个核心实体类User和Ware。User类包含username、password、role”admin”或”customer”三个字段Ware类则封装name、price、stock、category。注意这里没有用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(ListWare 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类管理它内部持有一个ListWare。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.txt3. 刷新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或11Swing在JDK 11仍完全兼容无需模块化配置。打开IntelliJ IDEA选择Open定位到解压后的项目根目录即包含.gitignore、src、backgroundPics的文件夹。IDEA会自动识别为Maven或Gradle项目吗不会。因为这是一个纯Java项目没有pom.xml或build.gradle。所以你要手动配置在Project StructureCtrlAltShiftS中Project Settings→Project→Project SDK选择你安装的JDKModules→Sources标签页将src文件夹标记为Sources蓝色图标resources如果有标记为ResourcesArtifacts标签页点击→JAR→From 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.txt和wares.txt默认不存在。这是故意为之的设计逼你理解“文件不存在”这个常见异常。你需要手动创建在项目根目录下新建文件夹usersInfo在usersInfo内新建空文件usersInfo.txt新建文件夹wares_data在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.javaclasspath根是项目根目录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面板类LoginPanel、CustomerMainFrame、AdminMainFrame都在此遵循“一个页面一个类”原则-src/目录核心业务类UserDAO、WareDAO、Cart、User、Ware纯粹的数据操作与GUI解耦-Test01.java至Test03.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会被视为不同用户。问题2UserDAO.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”-原因JTable的DefaultTableModel被设为null或allWares列表为null。-排查在AdminMainFrame构造方法里loadAllWares()后立刻加断点检查allWares.size()是否为0。如果是说明WareDAO.loadAllWares()抛异常被静默吞掉了。-解决在DAO方法里catch (IOException e)后必须e.printStackTrace()或log.error(加载商品失败, e)绝不能空catch5.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异步加载SwingWorkerListWare, Void worker new SwingWorker() { Override protected ListWare doInBackground() throws Exception { return WareDAO.loadAllWares(); // 耗时操作放这里 } Override protected void done() { try { allWares get(); // 获取结果 refreshTable(); // 在EDT中更新UI } catch (Exception e) { e.printStackTrace(); } } }; worker.execute();经验任何可能超过100ms的操作文件读写、网络请求、复杂计算都必须放到SwingWorker或ExecutorService中执行这是Swing响应性的生命线。5.3 项目结构与构建类问题打包后图片不显示、jar运行报错问题1用EX_2.0_jar.xml打包的jar双击运行闪退命令行运行显示“NoClassDefFoundError”-原因EX_2.0_jar.xml是IDEA的artifact配置它可能没把backgroundPics目录包含进jar包。-解决在Project Structure→Artifacts里展开你的jar条目点击→Directory Content选择backgroundPics文件夹确保它出现在jar包的根路径下。-验证用jar -tf your-app.jar | grep background查看jar包内容。问题2jar包运行后背景图加载为空白但控制台无报错-原因ImageIcon(backgroundPics/mainBackground.jpg)在jar包里找不到路径因为jar内路径是backgroundPics/mainBackground.jpg但代码试图在文件系统中找。-解决必须用getClass().getClassLoader().getResource()如前所述。这是最常被忽视的路径陷阱。问题3Test01.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-256SecureRandom生成盐。第二步敏感信息分离- 将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 功能扩展小步快跑验证你的架构能力不要一上来就加“会员积分”“促销活动”先做三个高价值小功能购物车持久化当前购物车关机就丢。新增cart_data/目录用户退出时自动保存购物车到cart_data/zhangsan_cart.txt下次登录时恢复。这教会你“会话状态管理”的本质操作日志在WareDAO的saveWares()方法里追加写入logs/operation.log记录“admin于2023-10-05 14:22:33 删除商品苹果”。用SimpleDateFormat格式化时间这是审计追踪的起点配置中心化将所有硬编码路径usersInfo/,wares_data/提取到config.properties文件用Properties.load()读取。这让你第一次体会到“配置与代码分离”的威力。6.4 工程化收尾从个人项目到可交付产品的最后一步一个能交付的桌面应用必须有安装包和自检能力制作Windows安装包用Inno Setup免费将jar包、backgroundPics、config.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、多窗口切换和简单权限分流逻辑。本文还有配套的精品资源点击获取