getWorkingMonth() 方法在首次调用时返回正确值,但第二次调用(如断言中)返回 0,根本原因是该方法直接修改了实例字段 currentSaving,导致后续调用基于已被清零或耗尽的状态重新计算。
`getworkingmonth()` 方法在首次调用时返回正确值,但第二次调用(如断言中)返回 0,根本原因是该方法直接修改了实例字段 `currentsaving`,导致后续调用基于已被清零或耗尽的状态重新计算。
在养老金规划器(PensionPlanner)这类金融计算场景中,查询类方法(如 getWorkingMonth())应具备幂等性与无副作用特性——即多次调用应返回相同结果,且不改变对象的可观察状态。而原实现中:
public int getWorkingMonth() {
double Saving = monthlySaving * (annualSalary / 12);
int months = 0;
while (currentSaving <p>每次调用都会持续累加并永久更新 currentSaving 字段。第一次 System.out.println(...) 调用后,currentSaving 已增长至 ≥ pensionTarget;第二次调用(断言中)进入 while 循环前条件 currentSaving </p><p>✅ 正确做法是:<strong>使用局部变量隔离计算过程,保护原始状态</strong>:</p><pre class="brush:php;toolbar:false;">public int getWorkingMonth() {
double monthlyContribution = monthlySaving * (annualSalary / 12);
double curr = currentSaving; // ✅ 复制初始值,不触碰字段
int months = 0;
while (curr <blockquote>
<p>? <strong>关键改进点</strong>: </p>
<ul>
<li>用 curr 替代 currentSaving 参与迭代计算; </li>
<li>原始字段 currentSaving 在整个过程中保持不变,保障方法幂等性; </li>
<li>变量命名更清晰(如 monthlyContribution 比 Saving 更具语义)。</li>
</ul>
</blockquote><p>⚠️ <strong>额外注意事项</strong>: </p>
- Java 中 assert 默认是禁用的(需运行时添加 -ea 参数),若未启用,断言不会执行——请确认测试环境已启用断言;
- 若业务逻辑本身需要“真实推进储蓄状态”,应显式提供 simulateOneMonth() 或 advanceToRetirement() 等有副作用的方法,而非隐藏在 getter 中;
- 对于高精度金融计算,建议后续考虑使用 BigDecimal 避免浮点误差累积(本例中 double 在月复利迭代下可能引入微小偏差,虽不影响 368 这一整数结果,但长期规划需谨慎)。
遵循“查询不改变状态”原则,不仅能修复断言失败,更是构建可预测、可测试、线程安全对象的基础实践。










