bankaccount 类不该把 balance 设为 public;应通过 deposit()、withdraw() 等方法控制修改入口,确保校验、日志和事务逻辑生效,防止直接赋值引发的余额突变问题。

BankAccount 类该不该把 balance 设为 public?
不该。直接暴露 balance 会绕过所有业务校验,比如取款时跳过余额不足检查,转账时跳过负数校验——这不是“方便”,是给 bug 开后门。
- 封装的核心不是“藏起来”,而是“控制修改入口”:所有对
balance的变更必须经过deposit()、withdraw()等方法,才能嵌入日志、风控或事务逻辑 - 如果子类(如
FixedDepositAccount)需要调整余额规则,应重写方法,而不是直接改字段 - 调试时发现余额突变?90% 是因为某处写了
account.balance = -100—— 这种代码在 private 字段 + getter/setter 下根本编译不过
transfer() 方法里 catch Exception 是不是太宽泛?
是,而且很危险。用 Exception 捕获会吞掉 NullPointerException 或 OutOfMemoryError 这类本该立即暴露的严重问题。
- 只捕获你明确能处理的异常:比如
InsufficientFundsException(自定义)、IllegalArgumentException(金额非法) - 数据库操作失败?抛
SQLException或包装成业务异常,别用通用Exception吞掉堆栈 - 如果真要兜底,也该在最外层(比如 main 或 controller),而不是在
transfer()内部静默吃掉
子类重写 withdraw() 时要不要调用 super.withdraw()?
看场景。继承不是为了“复用代码”,而是为了“复用契约”。如果子类逻辑和父类有本质差异(比如定期账户冻结期间禁止取款),就不该调用 super.withdraw()。
- 典型反例:在
FixedDepositAccount.withdraw()里先调super.withdraw()再抛异常——钱已经扣了,再报错就晚了 - 正确做法:完全重写,自己做余额检查 + 冻结状态检查 + 提前支取罚息计算
- 如果只是追加日志或统计,才考虑模板方法模式:把核心扣款逻辑抽成
doWithdraw(),让子类决定是否调用
测试转账失败时,只验证异常类型够不够?
不够。异常类型只是第一层,关键要看异常里有没有带上下文信息,比如具体哪个账户余额不足、当时余额多少。
- 坏示例:
assertThrows<insufficientfundsexception>(() -> account.transfer(to, 1000))</insufficientfundsexception>—— 不知道是转出方还是转入方的问题 - 好做法:捕获异常后检查
e.getMessage()是否包含"from balance: 200",或断言异常对象的getAvailableBalance()字段 - 容易漏的一点:转账是两个账户联动操作,测试必须覆盖“中途失败回滚”——比如转出成功但转入失败时,转出账户余额是否恢复?这得靠真实数据库事务或 mock 事务管理器验证
继承关系一旦拉长,异常路径和字段访问权限的组合爆炸就很难穷尽。比写对逻辑更难的,是让每个异常都携带足够诊断信息,且不被上层随意吞掉。








