Account类应封装余额状态与存取查行为:balance为private double,初始化校验非负;deposit(double amount)检查金额>0;withdraw(double amount)需同时验证金额>0且不超过余额。

如何用Java类建模银行账户核心行为
银行存款系统本质是状态(余额)与行为(存、取、查)的封装,Account 类必须隔离数据访问,禁止直接暴露 balance 字段。常见错误是把 balance 设为 public 或提供 setBalance() 这类破坏业务约束的接口。
正确做法是:
-
balance声明为private double,初始化时校验非负 - 存钱用
deposit(double amount),内部检查amount > 0,否则抛IllegalArgumentException - 取钱用
withdraw(double amount),必须同时检查amount > 0和amount - 查询余额只提供
getBalance(),不提供 setter
为什么不能用 float 处理金额,而必须用 BigDecimal 或 long
用 float 或 double 存储金额会导致精度丢失,比如 10.2 - 10.1 结果不是 0.1 而是 0.09999999999999998——这在金融计算中不可接受。
实际开发中两种主流方案:
立即学习“Java免费学习笔记(深入)”;
- 用
long表示“分”,例如 100 元存为10000L,所有运算都是整数加减,零误差,性能高 - 用
BigDecimal,但必须用字符串构造:new BigDecimal("100.50");绝不能用new BigDecimal(100.50)(后者会继承 double 的精度问题)
小项目用 long 更轻量;需要精确小数位(如汇率计算)再上 BigDecimal。
如何让多个账户共享统一的交易流水编号
每笔存款/取款操作应生成唯一、递增的流水号,且跨账户全局唯一。不能每个 Account 实例自己维护一个 counter,否则会重复。
推荐用静态变量 + 同步控制:
private static long nextTransactionId = 1; private static final Object idLock = new Object();public static long generateTransactionId() { synchronized (idLock) { return nextTransactionId++; } }
注意点:
- 不要用
static int,避免溢出;用long更安全 - 如果未来要分布式部署,这个方案失效,需换成数据库序列或雪花 ID
- 流水号生成和余额更新必须在同一个事务边界内,否则可能号已发、操作失败,造成空号
控制台交互部分最容易忽略的输入校验
用户从 Scanner 输入金额时,常直接调用 nextDouble(),但一旦输错(如字母、空行),程序就抛 InputMismatchException 并中断。
健壮写法是:
- 用
hasNextDouble()预检,不满足则nextLine()清缓冲区并提示重输 - 读入后立刻检查是否为正数,防止输
-100导致余额变负 - 对菜单选项用
nextInt()后紧跟nextLine()消除换行符残留,否则下一次nextLine()会读到空串
真实系统里,交互层只是胶水代码,重点永远在模型层的约束力和数据安全性上——哪怕界面简陋,只要 deposit() 和 withdraw() 内部校验牢靠,系统就不会出资金逻辑错误。










