ATM程序持续抛出NoSuchElementException异常,求解决方案
简易ATM程序NoSuchElementException问题解决指南
问题背景
开发简易ATM辅助程序时,菜单循环逻辑(显示菜单→等待输入→执行任务→再次显示菜单)持续抛出NoSuchElementException,且要求不在main方法中直接创建Scanner实例。
异常根源
- System.in流被提前关闭:
PasswordController.control()方法中调用了sc1.close(),直接关闭了全局唯一的System.in输入流。后续Operator类中创建的Scanner尝试读取已关闭的流,必然触发异常。 - 多余的nextLine()调用:两处代码都存在无意义的重复读取:
PasswordController中读取密码后额外调用sc1.nextLine(),会跳过用户一次输入,甚至在无输入时触发异常。Operator中显示菜单后先调用sc.nextLine()再读取选项,导致用户的第一次输入被跳过。
- 重复创建Scanner实例:
Operator的循环中每次都新建Scanner,既浪费资源,也增加了出错概率。
解决方案
核心规则
- 全局复用同一个Scanner实例,绝对不要关闭绑定System.in的Scanner(关闭后无法恢复输入流)。
- 删除所有多余的
nextLine()调用,保证输入读取逻辑和用户操作匹配。 - 通过构造函数注入传递Scanner实例,避免在main中直接暴露Scanner。
修改后代码示例
1. 重构PasswordController类
public class PasswordController { private Account account; private MessageService message; private Scanner scanner; // 通过构造函数注入Scanner public PasswordController(Account account, MessageService message, Scanner scanner) { this.account = account; this.message = message; this.scanner = scanner; } public boolean control() { boolean result = false; int count = 0; while(count < 3) { System.out.println("Enter password"); String password = scanner.nextLine(); // 移除多余的nextLine()调用 if (password.equals(account.getPassword())) { System.out.println("Password is correct."); result = true; count = 3; } else { message.badPassword(); count++; } } if(!result) { System.out.println("3 wrong tries, card is now frozen. Please contact the bank"); } // 禁止关闭scanner return result; } }
2. 重构Operator类
public class Operator { private Account account; private Operations op; private Scanner scanner; // 通过构造函数注入Scanner public Operator(Account account, Operations op, Scanner scanner) { this.account = account; this.op = op; this.scanner = scanner; } // 修正方法名拼写错误:operationSellect → operationSelect public void operationSelect() { boolean keepRunning = true; while(keepRunning) { System.out.println("Enter the operation number you want to do: "); System.out.println("1 : Show Balance"); // 修正拼写错误:Ballance → Balance System.out.println("2 : Withdraw cash"); System.out.println("3 : Deposit Money"); System.out.println("4 : Exit System"); System.out.println(""); // 移除多余的nextLine()调用 String option = scanner.nextLine(); switch(option) { case "1": op.showBalance(account); break; case "2": op.withdraw(account); break; case "3": op.deposit(account); break; case "4": keepRunning = false; break; default: System.out.println("Entered a wrong character, please enter again"); } } } }
3. 调整Main类实现依赖注入
public class Main { public static void main(String[] args) { // 创建全局唯一的Scanner实例 Scanner scanner = new Scanner(System.in); Account account = new Account(1234, "1234", 3000, 2); Card card = new Card("123456","Cash"); MessageService messageService = new MessageService(); CardReader cardReader = new CardReader(account , card, messageService); // 注入Scanner到PasswordController PasswordController passwordController = new PasswordController(account, messageService, scanner); Operations operations = new Operations(); // 注入Scanner到Operator Operator operator = new Operator(account, operations, scanner); ATM atm = new ATM(cardReader, passwordController, messageService, operations, operator, account, card); atm.start(); // 程序结束时统一关闭Scanner(可选,JVM退出时会自动处理) scanner.close(); } }
额外提示
- 永远不要关闭绑定
System.in的Scanner,除非确认程序后续完全不需要读取控制台输入。 - 所有需要读取输入的类都通过构造函数获取同一个Scanner实例,避免重复创建。
- 代码中修正了几处拼写错误,提升了整体可读性。
内容的提问来源于stack exchange,提问作者2brk
相关产品推荐
相关产品推荐

