Java收据总额计算程序InputMismatchException修复及优化咨询
收据计算程序问题修复方案
问题1:InputMismatchException 报错修复
这个异常本质是Scanner读取到的内容无法解析为Double类型,结合代码逻辑共有4个触发点,按优先级修复即可:
- 流初始化顺序错误:在创建Scanner读取文件前,提前创建了开启追加模式的写入流,会触发文件锁,导致Scanner读取到空值或非法内容。写入流不要提前初始化,等需要写文件的时候再创建。
- 写入模式配置错误:给
FileWriter构造方法传入了第二个参数true开启追加写入模式,每次写新值都会拼在旧内容后面,多次运行后receipt.txt里的内容会变成类似0.0012.305.20的拼接字符串,自然无法解析为数字。 - switch分支漏写
break:case 0(查看总额)分支执行完后没有中断,会直接穿透执行case1的逻辑,此时Scanner已经把文件里唯一的数值读走,再次调用nextDouble()会读到空内容触发异常。 - 输入解析逻辑错误:用户输入金额时用
Integer.parseInt做类型转换,只要用户输入带小数的金额就会抛出格式错误,应该改为Double类型解析。
问题2:文件覆写更新逻辑与更优实现方案
正确的覆写更新流程
不要同时打开读、写流操作同一个文件,按以下顺序执行即可实现累加:
- 程序启动时先判断
receipt.txt是否存在,不存在则创建文件并写入初始值0.00 - 仅创建读取流(Scanner)读取文件中存储的现有总额,读取完成后立刻关闭读取流,释放文件锁
- 处理用户选择:
- 选择查看总额:直接格式化读取到的总额弹窗展示即可
- 选择新增收据:获取用户输入的金额,和现有总额相加得到新的总金额
- 需要更新文件内容时,再创建
FileWriter,第二个参数传入false(或直接省略该参数)开启覆写模式,把新的总金额格式化后写入文件,写完关闭写入流 - 每个switch分支末尾都加上
break,避免逻辑穿透
更优实现方案
当前单值存储的方案能满足简单需求,但存在金额精度丢失、无对账记录、容错性差的问题,优化方向如下:
- 流资源统一用
try-with-resources语法创建,自动完成流关闭,避免资源泄漏和文件锁残留,不需要手动调用close() - 金额计算全部改用
BigDecimal类型,彻底避免double/float类型的浮点数精度丢失问题(比如0.1+0.2计算结果偏差的问题),是金额类场景的标准实践 - 存储逻辑优化:不要只存最终总额,改为每行存储一条收据记录(包含金额、记录时间、可选备注),每次启动时遍历所有记录累加得到总额,后续支持对账、删除错误记录的需求,容错性更高,单条记录损坏不会导致全量数据丢失
- 增加异常兜底:读取文件内容失败时自动重置初始值,用户输入非数字金额时弹窗提示重新输入,避免程序直接崩溃
修复后可直接运行的参考代码
import javax.swing.JOptionPane; import java.io.*; import java.util.Scanner; import java.text.DecimalFormat; import java.math.BigDecimal; class Receipts { public static void main(String[] args) throws IOException { DecimalFormat format = new DecimalFormat("0.00"); File file = new File("receipt.txt"); // 初始化文件,不存在则写入初始值0.00 if (!file.exists()) { try (PrintWriter initWriter = new PrintWriter(new FileWriter(file))) { initWriter.print("0.00"); } } // 先读取现有总额 BigDecimal total = BigDecimal.ZERO; try (Scanner scan = new Scanner(file)) { if (scan.hasNext()) { String totalStr = scan.next().trim(); try { total = new BigDecimal(totalStr); } catch (NumberFormatException e) { // 文件内容损坏时重置为0 total = BigDecimal.ZERO; } } } int choice = JOptionPane.showOptionDialog(null, "Hello. What would you like to do today?", "Receipts", JOptionPane.OK_CANCEL_OPTION, JOptionPane.INFORMATION_MESSAGE, null, new String[]{"See my total", "Add a receipt"}, "default"); switch(choice) { case 0: // 查看总额 JOptionPane.showMessageDialog(null, "Your total so far is $" + format.format(total) + "."); break; case 1: // 新增收据 try { String input = JOptionPane.showInputDialog("Enter the amount."); BigDecimal receipt = new BigDecimal(input.trim()); BigDecimal newTotal = total.add(receipt); // 覆写模式写入新总额 try (PrintWriter writer = new PrintWriter(new FileWriter(file, false))) { writer.print(format.format(newTotal)); } JOptionPane.showMessageDialog(null, "The amount has been added."); } catch (NumberFormatException e) { JOptionPane.showMessageDialog(null, "Invalid amount input, please enter a valid number."); } break; default: JOptionPane.showMessageDialog(null, "Please input your selection."); break; } } }
内容的提问来源于stack exchange,提问作者metal
相关产品推荐
相关产品推荐

