从ATM取款机到游戏菜单:用Java循环和Scanner打造你的第一个命令行交互程序(附完整代码)
从ATM取款机到游戏菜单:用Java循环和Scanner打造你的第一个命令行交互程序
当你第一次学习Java基础语法时,课本上的例子往往让人提不起兴趣——打印星星、计算数字,这些简单的练习很难让你感受到编程的乐趣。但你知道吗?这些看似枯燥的循环和条件语句,正是构建交互式程序的基础。本文将带你跳出课本习题,用while循环、Scanner输入和简单的控制逻辑,打造一个完整的命令行交互程序。
1. 为什么需要交互式程序?
在真实开发中,程序很少是单向执行的。想想你每天使用的软件:ATM机需要你输入密码和金额,游戏菜单需要你选择角色和装备,学生管理系统需要查询和修改数据。这些场景都有一个共同点——程序需要根据用户输入做出不同响应,并且持续运行直到用户主动退出。
传统课本上的循环练习(如打印10x10的星号矩阵)虽然能教会你语法,但缺乏真实感。让我们换个思路,用循环结构来实现这些实用场景:
// 传统练习:打印星号矩阵 for(int i=0; i<10; i++){ for(int j=0; j<10; j++){ System.out.print("*"); } System.out.println(); } // 交互式程序:游戏菜单框架 while(true){ showMenu(); int choice = getUserInput(); processChoice(choice); }前者只是机械地重复输出,后者却能创建真正的用户对话。这就是我们要追求的学习方式——用基础语法解决实际问题。
2. 构建交互程序的核心组件
2.1 循环结构的选择
Java提供了三种主要的循环结构,每种适合不同场景:
| 循环类型 | 特点 | 适用场景 |
|---|---|---|
while | 先判断条件后执行 | 不确定循环次数时(如菜单系统) |
do-while | 先执行一次再判断条件 | 至少需要执行一次时(如密码重试) |
for | 明确循环次数 | 遍历数组、固定次数的操作 |
对于交互程序,while循环是最自然的选择:
boolean running = true; while(running){ // 显示菜单 // 获取输入 // 处理逻辑 // 根据输入可能设置running=false }2.2 Scanner输入处理
java.util.Scanner是处理控制台输入的利器。关键方法包括:
nextInt(): 读取整数nextLine(): 读取整行文本nextDouble(): 读取浮点数
注意:混合使用不同Scanner方法时,要注意换行符的处理。建议在读取数字后调用
nextLine()消耗换行符。
2.3 流程控制技巧
break: 立即退出当前循环continue: 跳过本次循环剩余部分return: 从方法返回(如果在循环中)
这些控制语句能让你更灵活地管理程序流程。
3. 实战:游戏角色菜单系统
让我们用上述知识构建一个简易的游戏角色管理系统。功能包括:
- 查看角色属性
- 装备武器
- 使用道具
- 退出游戏
3.1 基础框架搭建
import java.util.Scanner; public class GameMenuSystem { public static void main(String[] args) { Scanner scanner = new Scanner(System.in); boolean gameRunning = true; // 角色初始属性 String characterName = "冒险者"; int health = 100; String weapon = "木剑"; int potions = 3; while(gameRunning) { System.out.println("\n=== 角色菜单 ==="); System.out.println("1. 查看角色属性"); System.out.println("2. 装备武器"); System.out.println("3. 使用道具"); System.out.println("4. 退出游戏"); System.out.print("请选择操作: "); int choice; try { choice = scanner.nextInt(); scanner.nextLine(); // 消耗换行符 } catch(Exception e) { System.out.println("输入无效,请输入数字1-4"); scanner.nextLine(); // 清除错误输入 continue; } // 处理用户选择 switch(choice) { case 1: // 查看属性逻辑 break; case 2: // 装备武器逻辑 break; case 3: // 使用道具逻辑 break; case 4: gameRunning = false; break; default: System.out.println("无效选择,请重新输入"); } } System.out.println("游戏结束,再见!"); scanner.close(); } }3.2 完善各功能模块
查看角色属性:
System.out.println("\n=== 角色属性 ==="); System.out.println("名称: " + characterName); System.out.println("生命值: " + health); System.out.println("当前武器: " + weapon); System.out.println("道具数量: " + potions);装备武器:
System.out.println("\n可选择的武器:"); System.out.println("1. 铁剑 (攻击力+10)"); System.out.println("2. 长弓 (攻击力+15)"); System.out.println("3. 法杖 (攻击力+20)"); System.out.print("选择要装备的武器: "); int weaponChoice = scanner.nextInt(); scanner.nextLine(); // 消耗换行符 switch(weaponChoice) { case 1: weapon = "铁剑"; break; case 2: weapon = "长弓"; break; case 3: weapon = "法杖"; break; default: System.out.println("无效选择,保持原武器"); } System.out.println("已装备: " + weapon);使用道具:
if(potions > 0) { System.out.println("使用道具,生命值恢复30点"); health += 30; potions--; System.out.println("剩余道具: " + potions); System.out.println("当前生命值: " + health); } else { System.out.println("没有可用道具了!"); }3.3 异常处理增强
良好的交互程序应该能处理各种异常输入:
// 在获取数字输入的地方添加try-catch try { choice = scanner.nextInt(); scanner.nextLine(); // 消耗换行符 } catch(Exception e) { System.out.println("输入无效,请输入数字1-4"); scanner.nextLine(); // 清除错误输入 continue; }4. 扩展思路:学生成绩查询系统
同样的框架可以轻松改造成其他实用程序。比如学生成绩查询系统:
import java.util.HashMap; import java.util.Map; import java.util.Scanner; public class StudentGradeSystem { public static void main(String[] args) { Scanner scanner = new Scanner(System.in); boolean systemRunning = true; // 模拟学生数据 Map<String, Integer> grades = new HashMap<>(); grades.put("张三", 85); grades.put("李四", 92); grades.put("王五", 78); while(systemRunning) { System.out.println("\n=== 学生成绩管理系统 ==="); System.out.println("1. 查询成绩"); System.out.println("2. 添加学生"); System.out.println("3. 修改成绩"); System.out.println("4. 退出系统"); System.out.print("请选择操作: "); int choice = scanner.nextInt(); scanner.nextLine(); // 消耗换行符 switch(choice) { case 1: System.out.print("输入学生姓名: "); String name = scanner.nextLine(); if(grades.containsKey(name)) { System.out.println(name + "的成绩是: " + grades.get(name)); } else { System.out.println("找不到该学生"); } break; case 2: System.out.print("输入新学生姓名: "); String newName = scanner.nextLine(); System.out.print("输入成绩: "); int newGrade = scanner.nextInt(); scanner.nextLine(); grades.put(newName, newGrade); System.out.println("添加成功"); break; case 3: System.out.print("输入要修改的学生姓名: "); String updateName = scanner.nextLine(); if(grades.containsKey(updateName)) { System.out.print("输入新成绩: "); int updatedGrade = scanner.nextInt(); scanner.nextLine(); grades.put(updateName, updatedGrade); System.out.println("修改成功"); } else { System.out.println("找不到该学生"); } break; case 4: systemRunning = false; break; default: System.out.println("无效选择"); } } System.out.println("系统已退出"); scanner.close(); } }这个例子展示了如何用相同的循环和输入处理框架,构建完全不同功能的交互程序。
5. 最佳实践与常见陷阱
5.1 交互程序设计原则
- 清晰的用户引导:每个步骤都要明确告诉用户可以做什么
- 一致的交互模式:保持输入输出风格一致
- 健壮的错误处理:预料可能的错误输入并妥善处理
- 简洁的界面:避免信息过载,合理分页
5.2 常见问题与解决方案
问题1:Scanner输入混乱
- 现象:混合使用nextInt()和nextLine()时出现跳过输入
- 解决:在nextInt()后立即调用nextLine()消耗换行符
int num = scanner.nextInt(); scanner.nextLine(); // 消耗换行符问题2:无限循环
- 现象:程序无法退出,一直循环
- 解决:确保有明确的退出条件,并测试所有分支
while(running) { // ... if(choice == EXIT_OPTION) { running = false; // 确保这个条件能被触发 } }问题3:代码重复
- 现象:相似的处理逻辑在多处重复
- 解决:将重复代码提取为方法
private static int getMenuChoice(Scanner scanner, int maxOption) { while(true) { try { int choice = scanner.nextInt(); scanner.nextLine(); if(choice >= 1 && choice <= maxOption) { return choice; } System.out.println("请输入1-" + maxOption + "之间的数字"); } catch(Exception e) { System.out.println("请输入有效的数字"); scanner.nextLine(); } } }6. 进一步优化方向
6.1 添加更多功能
- 保存/加载数据到文件
- 添加密码验证功能
- 实现更复杂的业务逻辑
6.2 改进用户体验
- 添加颜色输出(使用ANSI转义码)
- 实现多级菜单系统
- 添加ASCII艺术标题
public static void printTitle() { System.out.println(" ____ _ _ _ _ "); System.out.println(" / ___|| |_ __ _| |_(_)___| |"); System.out.println(" \\___ \\| __/ _` | __| / __| |"); System.out.println(" ___) | || (_| | |_| \\__ \\_|"); System.out.println(" |____/ \\__\\__,_|\\__|_|___(_)"); System.out.println(); }6.3 代码结构优化
- 使用面向对象设计,将不同功能封装到类中
- 使用枚举定义菜单选项
- 实现命令模式处理用户输入
enum MenuOption { VIEW_STATS("查看属性", 1), EQUIP_WEAPON("装备武器", 2), USE_ITEM("使用道具", 3), EXIT("退出游戏", 4); private String description; private int code; // 构造函数、getter方法等 } // 使用示例 for(MenuOption option : MenuOption.values()) { System.out.println(option.getCode() + ". " + option.getDescription()); }