Java Swing 实现ATM控制台输出重定向与GUI按钮输入
Swing版ATM交互程序改造实现方案
核心改造逻辑
- 完整保留现有
AtmProgram类的所有业务规则,不改动核心存款、取款、余额计算逻辑 - 把原本打印到控制台的输出流,重定向到界面上的
JTextArea文本域,完全替代控制台展示效果 - 移除所有
Scanner键盘输入逻辑,通过界面上的数字、确认、清除按钮维护输入缓存,完全替代物理键盘交互
具体实现步骤
1. 实现控制台输出重定向到文本域
注意:所有对Swing组件的操作必须放到事件 dispatch 线程中执行,否则会出现UI卡死、组件渲染异常的问题。自定义输出流中一定要用
SwingUtilities.invokeLater切换线程。
// 自定义输出流,承接原本所有控制台输出 private OutputStream guiOutputStream = new OutputStream() { @Override public void write(int b) { char currentChar = (char) b; // 切换到Swing UI线程更新文本域,避免线程安全问题 SwingUtilities.invokeLater(() -> { textArea.append(String.valueOf(currentChar)); // 自动滚动到文本末尾,展示最新输出 textArea.setCaretPosition(textArea.getDocument().getLength()); }); } };
界面初始化时,替换系统默认的标准输出、错误流:
PrintStream guiPrintStream = new PrintStream(guiOutputStream, true); System.setOut(guiPrintStream); // 替换System.out的输出目标 System.setErr(guiPrintStream); // 替换System.err的输出目标
完成这一步后,原来代码里所有的System.out.println、System.out.printf都会自动展示到文本域,不需要逐行修改原来的打印逻辑。
2. 实现按钮交互的输入缓存
用一个全局的字符串缓存,替代Scanner的标准输入源:
// 存储用户通过按钮输入的内容 private StringBuilder inputBuffer = new StringBuilder(); // 记录当前流程是否在等待用户提交输入 private boolean waitingForInput = false; // 存储用户已提交的输入值,供主流程读取 private String submittedInput = "";
给所有按钮绑定对应的事件逻辑:
- 数字按钮(0-9):点击时将对应数字追加到输入缓存,同时和控制台输入一样,把字符直接展示在文本域上
// 以数字1按钮为例,所有数字按钮逻辑完全一致 btn1.addActionListener(e -> { if (!waitingForInput) return; // 只有流程等待输入时才允许输入 inputBuffer.append("1"); textArea.append("1"); }); - 清除按钮:清空当前输入缓存,删除文本域上当前正在输入的内容,模拟退格/清除效果
correctionButton.addActionListener(e -> { inputBuffer.setLength(0); // 删除当前输入行的内容 int caretPos = textArea.getCaretPosition(); int lineStart = textArea.getLineStartOffset(textArea.getLineOfOffset(caretPos)); textArea.replaceRange("", lineStart, caretPos); }); - 确认按钮:将输入缓存的内容作为提交值,唤醒主流程的输入等待
entrerButton.addActionListener(e -> { if (!waitingForInput) return; submittedInput = inputBuffer.toString().trim(); inputBuffer.setLength(0); textArea.append("\n"); // 模拟按回车的换行效果 waitingForInput = false; // 标记输入已提交 }); - 退出按钮:直接触发程序退出逻辑,和原代码菜单选择“4.退出”的逻辑完全一致
quitterButton.addActionListener(e -> System.exit(0));
3. 重构主流程逻辑,适配GUI输入
原Main类中的ATM主循环不能直接在Swing事件线程中运行(会导致UI卡死),需要放到独立的后台线程执行,同时把原来从Scanner读输入的地方,替换为从我们的按钮输入缓存读取:
// 后台线程执行ATM主流程,不阻塞UI响应 new Thread(() -> { AtmProgram account = new AtmProgram(); while (true) { // 打印菜单的逻辑完全和原代码一致,因为输出已经重定向到文本域 System.out.println("************************************************"); System.out.println("1.Solde"); System.out.println("2.Dépôt"); System.out.println("3.Retrait"); System.out.println("4.Quitter"); System.out.println("************************************************"); System.out.print("Entrer Menu Option(et press Entrer):"); // 替代原来input.nextInt()的逻辑 int menuOption = readIntInput(); // 原代码中的switch分支逻辑完全复用,不需要修改 switch (menuOption) { // ... 所有case逻辑和原代码完全一致 } } }).start();
其中用于替代Scanner读输入的通用方法如下:
// 阻塞等待用户输入整数,直到拿到合法整数值 private int readIntInput() { while (true) { waitingForInput = true; // 等待用户提交输入 while (waitingForInput) { try { Thread.sleep(100); } catch (InterruptedException e) { Thread.currentThread().interrupt(); return -1; } } try { return Integer.parseInt(submittedInput); } catch (NumberFormatException e) { System.out.println("Option de Menu non valide"); } } } // 阻塞等待用户输入浮点数,用于存款、取款的金额输入 private double readDoubleInput() { while (true) { waitingForInput = true; while (waitingForInput) { try { Thread.sleep(100); } catch (InterruptedException e) { Thread.currentThread().interrupt(); return -1; } } try { return Double.parseDouble(submittedInput); } catch (NumberFormatException e) { System.out.println("Entrée non valide, veuillez entrer un nombre"); } } }
4. 细节适配说明
AtmProgram类不需要任何修改,类里所有的输出、异常抛出逻辑都会通过重定向的流自动展示到文本域- 如果需要支持输入带小数的金额,可以在界面上补充小数点按钮,绑定逻辑和数字按钮完全一致
- 原代码中退出时需要确认“O/N”的逻辑,可以直接复用字符串读入方法(和整数、浮点数读入逻辑一致),等待用户点击对应按钮提交即可
内容的提问来源于stack exchange,提问作者ettalha0x
相关产品推荐
相关产品推荐

