银行账户应封装为责任明确的实体,余额字段须private,仅通过deposit()和withdraw()校验后修改,构造方法拒绝负初始余额。

如何用 Java 类封装银行账户的核心行为
账户不是数据容器,而是有明确责任的实体。直接暴露 balance 字段或提供无校验的 setBalance() 方法,等于把取款机钥匙交给任意调用方。
- 必须用
private修饰余额字段,强制所有变更走业务方法 -
deposit(double amount)和withdraw(double amount)是唯二修改余额的入口,内部需校验金额正负、透支等逻辑 - 构造方法应拒绝负初始余额,例如:
public Account(String accountNumber, double initialBalance) { if (initialBalance < 0) { throw new IllegalArgumentException("Initial balance cannot be negative"); } this.accountNumber = accountNumber; this.balance = initialBalance; }
为什么不能用 double 表示货币金额
浮点数精度问题在金融场景下是致命的。比如 0.1 + 0.2 在 Java 中结果是 0.30000000000000004,不是数学意义上的 0.3。
- 必须使用
BigDecimal存储和计算金额,且构造时用字符串(new BigDecimal("100.50")),避免double构造器引入误差 - 所有加减乘除操作都调用
add()、subtract()、multiply()、divide()方法,并指定RoundingMode - 显示输出前用
setScale(2, RoundingMode.HALF_UP)统一保留两位小数
转账操作必须跨账户原子性处理
转账不是两个独立操作:A 减钱、B 加钱。中间若发生异常(如 A 扣款成功但 B 入账失败),会导致资金丢失或重复入账。
- 转账方法应定义在外部服务类(如
BankService)中,而非单个Account类内 - 必须用同步块或显式锁控制并发,防止多线程同时对同一账户操作引发竞态条件
- 推荐先校验双方账户有效性及余额充足性,再执行扣减与增加,任一环节失败则全部回滚(实际项目中需结合事务管理器,简易版可用布尔返回值+状态重置)
账户编号生成与唯一性保障的实操陷阱
用 Math.random() 或简单递增整数生成账号,在多实例或高并发下极易冲突。
立即学习“Java免费学习笔记(深入)”;
- 开发阶段可用
UUID.randomUUID().toString().replace("-", "").substring(0, 10)快速生成伪唯一 ID - 生产环境必须依赖数据库主键自增、Snowflake 算法或分布式 ID 生成服务
- 若用内存集合(如
Map)做账户仓库,添加新账户前必须检查map.containsKey(accountNumber),否则覆盖已有账户










