尧图网络科技YAOTU DIGITAL 获取报价
获取报价
首页 / 资讯中心 / 文章详情

Java二维数组模拟酒店房间管理实战

发布时间:2026/9/23 23:32:31

资讯中心
01
ARTICLE

Java二维数组模拟酒店房间管理实战

Java二维数组模拟酒店房间管理实战
简介这是一套面向Java初学者的酒店管理系统实战源码适用于高校课程设计、自学项目实践及Java基础技能巩固。系统以酒店前台业务为场景基于Java SE实现房间预订、退房、状态查询等核心功能采用二维数组模拟酒店结构封装Room类管理房间编号、类型与空闲状态并集成AJAX技术提升Web交互体验。资源包共1834个文件含60个Java源文件、190个Class字节码、574个JS脚本、657个GIF图标及132个PNG图片辅以JSP、CSS、XML和SQL等配套文件完整覆盖前后端开发与数据库操作压缩后大小为15.87MB。已有2309人学习下载提供可直接部署运行的完整工程访问地址http://localhost:8080/JavaPrj_9/包含web.xml配置说明、数据库脚本及典型工具类如StringUtils、ArrayUtils、FastDateFormat等便于理解面向对象设计、数组应用与Web层整合逻辑。1. 这不是玩具项目一个用二维数组硬刚酒店业务逻辑的 Java 实战源码含可直接启动的 MySQL 数据库脚本你见过用Room[][] rooms二维数组模拟整栋酒店楼层房型布局的系统吗不是 Spring Boot MyBatis Vue 的标配三件套也不是微服务拆分后的抽象模型——它就用最朴素的 Java 基础语法for循环遍历楼层、if-else判断空闲状态、Scanner接收前台输入把“订房→退房→查状态”三个核心动作压进不到 200 行主逻辑里。这不是教学 Demo而是作者花了 8 个月、迭代 8 稿、在导师手把手指导下打磨出的真实可运行系统。它不炫技但每行代码都踩过坑比如二维数组索引越界导致退房失败却无提示比如Room对象未重写toString()导致打印状态时全是内存地址比如web.xml中 servlet 映射路径写错半字符首页http://localhost:8080/JavaPrj_9/死活打不开。它面向的是刚学完 Java 基础、正卡在“怎么把 if 和 for 变成业务”的新手也适合想快速验证 MVC 分层是否真能跑通的老手——因为它的BusinessServiceImpl层干净得像张白纸没有 Spring AOP 的黑匣子没有事务传播的玄学只有room.setAvailable(false)这种一眼看懂的因果链。数据库部分更实在.rar包里直接塞了.sql文件建库、建表、插测试数据标准双床房/大床房/套房共 36 间mysql -u root -p hotel_init.sql一行就能喂饱 Tomcat。这不是“Java 面试题八股文”是能让你在命令行敲出java -cp .;lib/* MainApp就看到菜单跳出来的血泪经验。2. 从解压到首页5 分钟跑通这个基于二维数组的酒店系统2.1 解压与目录结构还原看清.rar包里的真实骨架别急着导入 IDE。先用 WinRAR 或 7-Zip 解压java_酒店管理系统源码下载含数据库.rar你会看到三层结构JavaPrj_9/ ├── src/ │ ├── com/.../service/impl/BusinessServiceImpl.class │ ├── com/.../entity/Room.java ← 关键这是房间对象定义 │ └── com/.../main/MainApp.java ← 控制台版入口 ├── WebContent/ │ ├── WEB-INF/web.xml ← Servlet 配置中枢 │ ├── userLogin.jsp ← 登录页注意不是 index.jsp │ └── ... ← JSP 页面群 ├── lib/ │ └── mysql-connector-java-5.1.47.jar ← JDBC 驱动版本锁定 └── hotel_init.sql ← 数据库初始化脚本提示src/下全是.class文件别慌——这是编译后的字节码但MainApp.java和Room.java的源码其实藏在src/com/.../main/和src/com/.../entity/的同名.java文件里部分版本会混放。若找不到.java说明作者只发布了 class此时需反编译推荐 JD-GUI重点看Room类字段和BusinessServiceImpl的bookRoom()方法逻辑。2.2 数据库初始化用hotel_init.sql一键建库建表打开 MySQL 命令行或 Navicat/SQLyog# 1. 创建数据库编码必须为 utf8mb4否则中文乱码 CREATE DATABASE hotel_db CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; # 2. 切换数据库 USE hotel_db; # 3. 执行初始化脚本路径替换成你解压后的真实路径 SOURCE /path/to/your/JavaPrj_9/hotel_init.sql;hotel_init.sql内容精简但关键-- 建表rooms 表对应 Room 对象的持久化 CREATE TABLE rooms ( id INT PRIMARY KEY AUTO_INCREMENT, room_number VARCHAR(10) NOT NULL, -- 房间编号如 101, 205 room_type ENUM(标准间,大床房,套房) DEFAULT 标准间, is_available TINYINT(1) DEFAULT 1, -- 1空闲0已入住 create_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP ); -- 插入 36 间测试房按 3 楼 × 12 间设计覆盖所有类型 INSERT INTO rooms (room_number, room_type, is_available) VALUES (101,标准间,1),(102,标准间,1),...,(312,套房,1);参数说明is_available字段用TINYINT(1)而非BOOLEAN是因为 MySQL 5.7 以下版本不真正支持布尔类型底层仍是整数。room_number设为VARCHAR是为兼容 B101 这类带字母的编号避免INT类型无法存储。2.3 Tomcat 部署修改web.xml绑定首页入口WebContent/WEB-INF/web.xml是整个 Web 应用的神经中枢。找到welcome-file-list部分welcome-file-list welcome-fileuserLogin.jsp/welcome-file !-- 注意不是 index.jsp -- /welcome-file-list同时确认servlet-mapping是否将LoginServlet映射到/loginservlet-mapping servlet-nameLoginServlet/servlet-name url-pattern/login/url-pattern !-- 前端 form action/login 必须匹配 -- /servlet-mapping逻辑说明系统采用传统 Servlet/JSP 模式。用户访问http://localhost:8080/JavaPrj_9/时Tomcat 自动加载userLogin.jsp提交表单后请求发往/loginURL由LoginServlet处理并跳转至主页面main.jsp。若web.xml中welcome-file写成index.jsp则首页直接 404。2.4 启动验证控制台版与 Web 版双路径确认功能路径一纯 Java 控制台版验证核心逻辑进入src/目录编译并运行# 编译需包含当前目录和 lib 下的 jar javac -cp .;lib/mysql-connector-java-5.1.47.jar com/example/main/MainApp.java # 运行classpath 同样要加 jar java -cp .;lib/mysql-connector-java-5.1.47.jar com.example.main.MainApp你会看到清晰菜单 酒店管理系统 1. 预订房间 2. 退房 3. 查看所有房间状态 4. 退出 请选择输入3应打印出类似楼层 1: [101:标准间-空闲] [102:标准间-已入住] ... 楼层 2: [201:大床房-空闲] ...路径二Web 版验证 MVC 流程将JavaPrj_9/整个文件夹复制到 Tomcat 的webapps/目录下启动 Tomcat# Linux/Mac ./tomcat/bin/startup.sh # Windows tomcat\bin\startup.bat浏览器访问http://localhost:8080/JavaPrj_9/输入默认账号admin/123456密码通常写死在LoginServlet.java的doPost方法里成功跳转至main.jsp—— 这里才是 AJAX 版本的交互界面点击“预订客房”按钮后台通过BookRoomServlet调用BusinessServiceImpl.bookRoom()最终更新数据库。关键区别控制台版用Room[][]数组内存操作Web 版用 JDBC 操作数据库。二者BusinessServiceImpl的方法签名一致如public boolean bookRoom(String roomNumber)但内部实现不同——前者查二维数组后者查 SQL。这种设计让新手一眼看清“业务逻辑”与“数据存储”的分离。3. 核心业务逻辑拆解二维数组模拟与数据库驱动的双轨实现3.1Room[][]二维数组的设计哲学用空间换时间的物理建模系统用Room[][] rooms new Room[3][12];模拟 3 层楼、每层 12 间房的物理布局。Room类极简public class Room { private String roomNumber; // 101, 205 private String roomType; // 标准间, 大床房 private boolean isAvailable; // true空闲 // 构造方法、getter/setter 省略... }初始化发生在MainApp.java的initRooms()方法private static void initRooms() { for (int floor 0; floor rooms.length; floor) { for (int roomIndex 0; roomIndex rooms[floor].length; roomIndex) { // 计算房间号floor0 → 1xx, floor1 → 2xx, floor2 → 3xx String roomNum (floor 1) String.format(%02d, roomIndex 1); // 101, 102... String type getTypeByIndex(roomIndex); // 0-3:标准间, 4-7:大床房, 8-11:套房 rooms[floor][roomIndex] new Room(roomNum, type, true); } } }参数说明String.format(%02d, roomIndex 1)确保房间号始终两位数01,02避免101和11混淆。getTypeByIndex()是硬编码规则前 4 间为标准间中间 4 间为大床房最后 4 间为套房——这正是课程设计要求的“用数组知识模拟业务”。3.2BusinessServiceImpl的双模式实现内存操作 vs 数据库操作该类是业务逻辑中枢方法签名统一但实现分叉内存版控制台调用public boolean bookRoom(String roomNumber) { // 解析房间号提取楼层 floor1, 房间序号 num1 → rooms[0][0] int floor Integer.parseInt(roomNumber.substring(0, 1)) - 1; int roomIndex Integer.parseInt(roomNumber.substring(1)) - 1; if (floor 0 floor rooms.length roomIndex 0 roomIndex rooms[floor].length) { if (rooms[floor][roomIndex].isAvailable()) { rooms[floor][roomIndex].setAvailable(false); return true; } } return false; // 房间不存在或已占用 }数据库版Web Servlet 调用public boolean bookRoom(String roomNumber) { String sql UPDATE rooms SET is_available 0 WHERE room_number ?; try (Connection conn DriverManager.getConnection(url, user, pwd); PreparedStatement ps conn.prepareStatement(sql)) { ps.setString(1, roomNumber); return ps.executeUpdate() 0; // 影响行数 0 即成功 } catch (SQLException e) { e.printStackTrace(); return false; } }逻辑说明两种实现共享同一接口但内存版依赖数组索引计算数据库版依赖 SQLWHERE条件。这解释了为何web.xml中BusinessServiceImpl的 Bean 配置会指定不同的实现类——实际项目中可通过 Spring Profile 切换但本源码用硬编码区分。3.3 AJAX 交互流程main.jsp如何无刷新操作房间main.jsp中的关键 JavaScriptfunction bookRoom(roomNum) { $.ajax({ url: BookRoomServlet, // 对应 web.xml 中的 servlet-mapping type: POST, data: { roomNumber: roomNum }, // 发送房间号 success: function(response) { if (response success) { alert(预订成功); location.reload(); // 刷新页面更新状态 } else { alert(预订失败 response); } } }); }对应的BookRoomServlet.javaprotected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { String roomNumber request.getParameter(roomNumber); BusinessServiceImpl service new BusinessServiceImpl(); // 关键这里调用的是数据库版 bookRoom() boolean result service.bookRoom(roomNumber); if (result) { response.getWriter().write(success); } else { response.getWriter().write(房间不存在或已占用); } }参数说明request.getParameter(roomNumber)获取前端传来的roomNumber必须与 AJAX 的data键名完全一致。response.getWriter().write()直接输出字符串给前端success是约定返回值main.jsp的success回调函数据此判断。4. 避坑指南8 稿迭代踩出的 5 个致命细节4.1 现象控制台版输入 101 预订成功但 Web 版同样操作返回 房间不存在原因BookRoomServlet中BusinessServiceImpl实例化时未注入数据库连接参数导致DriverManager.getConnection()使用了错误的 URL 或空凭证executeUpdate()返回 0 行影响却未抛异常因catch块仅printStackTrace()且未返回错误信息。解决检查BusinessServiceImpl的数据库连接初始化。正确做法是在构造方法中传入连接参数public BusinessServiceImpl(String url, String user, String pwd) { this.url url; this.user user; this.pwd pwd; } // 在 BookRoomServlet 中 BusinessServiceImpl service new BusinessServiceImpl( jdbc:mysql://localhost:3306/hotel_db?useSSLfalseserverTimezoneUTC, root, your_password);4.2 现象main.jsp点击“查看所有房间”按钮页面显示空白或报 500 错误原因ShowAllRoomsServlet的doGet方法中查询 SQL 写成SELECT * FROM rooms但rooms表在 MySQL 中被创建为小写而某些 Linux 系统的 MySQL 默认开启lower_case_table_names1导致表名大小写敏感查询失败。解决统一使用小写表名并在 SQL 中显式指定String sql SELECT room_number, room_type, is_available FROM rooms ORDER BY room_number; // 或更稳妥在建表语句中强制小写 CREATE TABLE rooms ( ... ); // 反引号包裹4.3 现象userLogin.jsp提交后跳转main.jsp但页面顶部显示HTTP Status 404 – /JavaPrj_9/main.jsp原因web.xml中welcome-file-list设置了userLogin.jsp但LoginServlet的response.sendRedirect(main.jsp)是相对路径跳转而main.jsp实际位于WebContent/根目录Servlet 容器认为它在WEB-INF/下因web.xml默认视图解析器路径。解决在LoginServlet中使用绝对路径跳转response.sendRedirect(request.getContextPath() /main.jsp); // request.getContextPath() 返回 /JavaPrj_9拼接后为 /JavaPrj_9/main.jsp4.4 现象二维数组版initRooms()初始化后打印房间状态时所有roomType都是null原因Room类的roomType字段在构造方法中未赋值或getTypeByIndex()方法返回null如roomIndex15超出范围。解决检查getTypeByIndex()的边界private static String getTypeByIndex(int index) { if (index 0 index 4) return 标准间; else if (index 4 index 8) return 大床房; else if (index 8 index 12) return 套房; else return 未知类型; // 防御性返回避免 null }4.5 现象MySQL 8.0 启动时报错java.sql.SQLException: Unknown system variable query_cache_size原因mysql-connector-java-5.1.47.jar是为 MySQL 5.x 设计的驱动与 MySQL 8.0 不兼容8.0 移除了查询缓存相关变量。解决升级 JDBC 驱动。下载mysql-connector-java-8.0.22.jar替换lib/下旧 jar并修改Class.forName(com.mysql.cj.jdbc.Driver)注意包名变化// MySQL 8.0 必须用新驱动类名 Class.forName(com.mysql.cj.jdbc.Driver); String url jdbc:mysql://localhost:3306/hotel_db?useSSLfalseserverTimezoneUTCallowPublicKeyRetrievaltrue;5. 进阶验证用单元测试覆盖核心业务避免“改一处崩一片”5.1 为什么必须写单元测试——二维数组的脆弱性暴露无遗当你修改initRooms()的楼层逻辑比如从 3 层改成 4 层或者调整getTypeByIndex()的区间划分控制台版可能瞬间崩溃ArrayIndexOutOfBoundsException直接终止程序而 Web 版因有 try-catch 只是静默失败。没有测试你永远不知道bookRoom(401)是否真的生效。我习惯在src/test/下建RoomServiceTest.java用 JUnit 4 验证三大核心场景public class RoomServiceTest { private BusinessServiceImpl service; Before public void setUp() { // 初始化内存版 rooms避免依赖数据库 service new BusinessServiceImpl(); service.initRooms(); // 调用控制台版初始化 } Test public void testBookRoom_Success() { assertTrue(service.bookRoom(101)); // 预订 101 assertFalse(service.isRoomAvailable(101)); // 确认已占用 } Test public void testBookRoom_InvalidRoom() { assertFalse(service.bookRoom(999)); // 不存在的房间号 } Test public void testBookThenCancel() { service.bookRoom(101); assertTrue(service.cancelRoom(101)); // 假设有 cancelRoom 方法 assertTrue(service.isRoomAvailable(101)); } }逻辑说明Before注解确保每个测试用例前都重建干净的rooms数组避免状态污染。assertTrue()和assertFalse()是断言核心比System.out.println()更可靠——测试失败时 IDE 会高亮报错行而不是等你肉眼扫描控制台。5.2 数据库操作的测试陷阱如何避免“测试通过生产炸锅”JDBC 测试最大的坑是连接泄漏。下面这段代码看似正确实则危险Test public void testDBBookRoom() { Connection conn null; try { conn DriverManager.getConnection(url, user, pwd); // ... 执行 SQL } catch (SQLException e) { fail(e.getMessage()); } // 忘记 close(conn)连接池耗尽后后续测试全挂 }正确写法Java 7 try-with-resourcesTest public void testDBBookRoom() { String sql UPDATE rooms SET is_available 0 WHERE room_number ?; try (Connection conn DriverManager.getConnection(url, user, pwd); PreparedStatement ps conn.prepareStatement(sql)) { ps.setString(1, 101); assertTrue(ps.executeUpdate() 0); } catch (SQLException e) { fail(数据库操作失败: e.getMessage()); } }参数说明try-with-resources语法自动调用conn.close()和ps.close()即使发生异常也不遗漏。这是 Java 基础中“资源管理”的硬性要求也是面试官常问的finally与try-with-resources对比点。5.3 一份可直接复用的测试报告模板表格化测试用例输入预期输出实际结果状态备注预订有效房间101truetrue✅101 初始为空闲预订已占用房间101已预订falsefalse✅状态检查生效预订不存在房间999falsefalse✅边界防护到位退房有效房间101已预订truetrue✅cancelRoom()方法存在数据库查询总数SELECT COUNT(*) FROM rooms3636✅初始化脚本正确技巧每次修改BusinessServiceImpl后先运行此表所有用例。如果testBookRoom_Success失败说明核心逻辑崩了如果testDBBookRoom失败说明数据库配置或 SQL 有问题。这种分层验证比反复启停 Tomcat 效率高 10 倍。从那以后我每次重构Room类的字段或者调整web.xml的 servlet 映射都强制走一遍这 5 个单元测试用例——不是为了交差而是给自己留一张“后悔药”当新功能上线后用户投诉“订不了房”我打开 IDE 5 秒内就能定位是内存逻辑还是数据库链路的问题。这份源码的价值不在它多先进而在它把 Java 基础的“坑”摊开给你看让你在Room[][]的二维世界里亲手把if、for、JDBC这些散装零件拧成一台能喘气的酒店系统。希望帮到你。本文还有配套的精品资源点击获取
02
RELATED NEWS

相关资讯

更多网站建设与数字化升级内容

03
WHY YAOTU

想打造同款高转化官网?

懂行业、懂生意,从建站到增长一站式陪跑

场景化定制

不做模板站,围绕你的业务场景量身设计,小众不撞款。

营销型架构

以转化目标组织内容与路径,让官网真正带来询盘。

全周期服务

设计、开发、运营、运维一体,上线只是开始。

免费获取你的建站方案

留下需求,专属顾问 24 小时内为你输出方案建议。