用Scanner+ArrayList实现记账核心:定义Transaction类含amount、type、note、timestamp字段;输入时统一用nextLine()防换行残留;查账用LocalDateTime时间比较而非字符串匹配;退出时用Files.write按行存文本,分隔符为|。

用 Scanner + ArrayList 做最简记账核心
控制台记账程序不需要框架,Java 标准库完全够用。关键不是“怎么炫技”,而是把 收支类型、金额、备注、时间 四个字段稳稳存住、查得快、删得准。
别一上来就搞数据库或文件持久化——先用 ArrayList 在内存里跑通流程。定义一个 Transaction 类,至少包含:
-
double amount(别用float,精度问题会出错) -
String type(建议固定为"income"或"expense") String note-
LocalDateTime timestamp(用java.time,别用过时的Date)
用户输入用 Scanner 逐行读取,注意调用 scanner.nextLine() 消掉换行残留,否则后续 nextLine() 会跳过输入。
避免 Scanner.nextLine() 吃掉输入的坑
这是新手写控制台程序最常卡住的地方:用 scanner.nextDouble() 或 scanner.nextInt() 读完数字后,紧接着 scanner.nextLine() 会立刻返回空字符串——因为前一个方法没消费掉回车符。
立即学习“Java免费学习笔记(深入)”;
解决办法统一加一行“清缓存”:
scanner.nextDouble(); scanner.nextLine(); // ← 必须加这一行 String note = scanner.nextLine();
更稳妥的做法是全程只用 nextLine(),再手动解析数字:
String amountStr = scanner.nextLine(); double amount = Double.parseDouble(amountStr); // 加 try-catch 处理异常
这样逻辑一致,不易漏。
按日期范围查账单时别手写字符串比较
用户说“查 2024-03-01 到 2024-03-15 的支出”,如果把 timestamp.toString() 当成字符串去 contains 或 substring 截取,会出错且难维护。
正确做法是用 LocalDateTime 的时间比较能力:
LocalDateTime start = LocalDate.parse("2024-03-01").atStartOfDay();
LocalDateTime end = LocalDate.parse("2024-03-15").plusDays(1).atStartOfDay();
for (Transaction t : records) {
if (t.timestamp.isAfter(start) && t.timestamp.isBefore(end)) {
System.out.println(t);
}
}
注意:用 isBefore/isAfter 而非 ,避免毫秒级误差;结束时间设为次日 0 点,才能包含 3 月 15 日全天。
退出前把数据存到文件,但别用 FileWriter 直接写对象
程序退出时把 ArrayList 存成文件,目的是下次启动还能读。别用 FileWriter 拼 JSON 或 CSV 字符串——容易格式错乱、中文乱码、字段含逗号就崩。
推荐方案:用 ObjectOutputStream 序列化(仅限开发练手),或更实用的 Files.write() 写纯文本行:
Listlines = records.stream() .map(t -> String.format("%s|%s|%.2f|%s", t.timestamp, t.type, t.amount, t.note)) .collect(Collectors.toList()); Files.write(Paths.get("ledger.txt"), lines, StandardCharsets.UTF_8);
读的时候用 Files.readAllLines() + String.split("\\|") 解析。分隔符选 | 比逗号安全,但注意 note 里如果真有 |,就得换成更少见的符号(比如 \u0001)或改用 Jackson 写 JSON。
真正上线项目肯定要 SQLite 或 H2,但控制台练手阶段,文件按行存是最可控、最容易调试的方式。










