
本文揭示了因误用实例变量导致的“库存不递减”问题,指出多个对象各自维护独立状态是症结所在,并通过对比实例变量与静态变量的行为,提供可立即落地的修复方案。
本文揭示了因误用实例变量导致的“库存不递减”问题,指出多个对象各自维护独立状态是症结所在,并通过对比实例变量与静态变量的行为,提供可立即落地的修复方案。
在您实现的果汁店系统中,JuiceStore 类的 leftJuices 字段被声明为实例变量(instance variable):
private int leftJuices = 3;
这意味着:每个 JuiceStore 对象都拥有自己独立的一份 leftJuices 副本。当您创建三个对象:
JuiceStore Juice1 = new JuiceStore(14); JuiceStore Juice2 = new JuiceStore(7); JuiceStore Juice3 = new JuiceStore(17);
→ 实际上生成了 3 个互不相关的库存计数器,初始值均为 3。
调用 Juice1.buyJuice() 时,仅 Juice1.leftJuices 减为 2;
调用 Juice2.buyJuice() 时,仅 Juice2.leftJuices 减为 2;
同理,Juice3 的库存也独立从 3 → 2。
因此,程序输出看似“每次都剩 2 瓶”,实则是三个平行世界各自完成了“第一次购买”,而非共享同一库存池——这显然违背了现实业务逻辑(一家店共 3 瓶果汁,卖出 1 瓶后应剩 2 瓶,再卖 1 瓶应剩 1 瓶,而非每笔订单都重置为“还剩 2 瓶”)。
✅ 正确解法:使用静态变量(static)共享状态
若库存属于整个果汁店(即所有订单共用同一库存),则 leftJuices 应声明为 static:
立即学习“Java免费学习笔记(深入)”;
public class JuiceStore {
private int temperature;
private static int leftJuices = 3; // ✅ 全局共享,所有实例共用同一变量
public JuiceStore(int temperature) {
this.temperature = temperature;
}
public void buyJuice() throws NoJuiceException, TooColdException, TooWarmException {
if (leftJuices < 1) {
throw new NoJuiceException("Unfortunately, there is no juice left. Come back tomorrow.");
}
leftJuices--; // ✅ 直接操作静态变量(无需 this.)
System.out.println("You have bought a juice, there are " + leftJuices + " left.");
if (temperature < 9) {
throw new TooColdException("The juice is too cold.");
}
if (temperature > 15) {
throw new TooWarmException("The juice is too warm.");
}
System.out.println("Drink successful.");
}
}? 关键变化说明:
- private static int leftJuices = 3; —— 静态字段属于类本身,而非某个对象;
- leftJuices--; —— 访问静态变量时不应加 this.(编译器会警告);
- 所有 JuiceStore 实例调用 buyJuice() 时,操作的是内存中唯一一份 leftJuices。
⚠️ 注意事项与设计权衡
-
线程安全风险:static 变量在多线程环境下存在竞态条件(如并发购买可能导致库存超卖)。生产环境应配合同步机制(如 synchronized 方法或 AtomicInteger):
private static AtomicInteger leftJuices = new AtomicInteger(3); // ... if (leftJuices.decrementAndGet() < 0) { /* 处理售罄 */ } 单例 vs 静态字段:若需更清晰的职责分离(如统一管理库存、日志、配置),建议将库存逻辑提取至独立的 InventoryManager 单例类,而非依赖静态字段——这更符合面向对象设计原则(单一职责、可测试性)。
构造函数参数含义澄清:当前 new JuiceStore(14) 中的 14 表示温度,但类名 JuiceStore 易被误解为“店铺实例”。更准确的命名如 ChilledJuice 或增加注释,可提升代码可读性。
✅ 总结
| 场景 | 变量类型 | 是否共享 | 适用性 |
|---|---|---|---|
| 每瓶果汁有独立保质期/批次号 | 实例变量(private int) | ❌ 否 | ✅ 正确 |
| 全店总库存数量 | 静态变量(private static int) | ✅ 是 | ✅ 本问题正确解法 |
| 需跨线程安全更新库存 | AtomicInteger / 同步块 | ✅ 是(线程安全) | ? 生产推荐 |
修复后,三次连续调用 buyJuice() 将依次输出:
"… there are 2 left." → "… there are 1 left." → "… there are 0 left.",
真正实现库存的全局、有序、可预测递减。










