
本文讲解如何通过添加公共方法(如复制构造器、除法运算等)来避免直接访问私有成员,从而在project类中安全调用weight的内部逻辑,真正实现面向对象的封装设计。
本文讲解如何通过添加公共方法(如复制构造器、除法运算等)来避免直接访问私有成员,从而在project类中安全调用weight的内部逻辑,真正实现面向对象的封装设计。
在面向对象编程中,封装的核心目标不是“隐藏一切”,而是将数据与行为统一管理,并仅通过受控的公共接口暴露必要能力。当前代码中,Project 类试图直接调用 Weight 的私有方法 toOunces() 和访问静态常量 OUNCES_IN_A_POUND,这虽能运行但严重破坏了封装——外部类被迫了解并依赖 Weight 的内部单位换算细节,一旦未来修改为千克制或增加精度处理,所有外部调用点都将崩溃。
正确的做法是:让 Weight 类自身承担全部重量相关的计算职责,对外只提供语义清晰、职责单一的公共方法。以下是关键改进步骤:
✅ 第一步:为 Weight 添加必要的公共方法
// 1. 复制构造器:支持创建现有 Weight 的独立副本
public Weight(Weight other) {
if (other == null) throw new IllegalArgumentException("Weight cannot be null");
this.pounds = other.pounds;
this.ounces = other.ounces;
// normalize() 可选,因原始对象已规范化;为安全起见建议调用
this.normalize();
}
// 2. 新增除法方法:返回新 Weight 实例(推荐不可变风格)
public Weight divideBy(double divisor) {
if (divisor == 0) throw new ArithmeticException("Cannot divide by zero");
double totalOunces = this.toOunces(); // 内部调用私有方法,完全合法
double resultOunces = totalOunces / divisor;
int resultPounds = (int) (resultOunces / OUNCES_IN_A_POUND);
double resultOuncesRemainder = resultOunces % OUNCES_IN_A_POUND;
return new Weight(resultPounds, resultOuncesRemainder);
}
// 3. 提升 toOunces() 为 public —— 仅当业务需要暴露总量时才开放(谨慎!)
// 更推荐的方式是提供更高层语义方法,如 compareTo() 或 isLessThan()
// 但若必须比较数值大小,可改为:
public int compareTo(Weight other) {
double thisTotal = this.toOunces();
double otherTotal = other.toOunces();
return Double.compare(thisTotal, otherTotal);
}? 注意:toOunces() 本身是合理的内部工具方法,不应被外部直接使用。若 Project 需要比较大小,应优先调用 lessThan()(已有)或新增 compareTo();若需做算术运算(如求平均),应由 Weight 提供 add()、divideBy() 等组合操作,而非暴露底层单位。
✅ 第二步:重构 Project 类,完全脱离对私有成员的依赖
private static Weight findMaximum(Weight weight1, Weight weight2, Weight weight3) {
// 使用已有 public 方法 lessThan,无需接触 toOunces()
Weight max = weight1;
if (weight2.lessThan(max) == false) max = weight2;
if (weight3.lessThan(max) == false) max = weight3;
return max;
}
private static Weight findAverage(Weight weight1, Weight weight2, Weight weight3) {
// 利用 Weight 自身完成加法与除法
Weight sum = new Weight(weight1); // 复制构造器
sum.addTo(weight2);
sum.addTo(weight3);
return sum.divideBy(3.0); // 封装好的除法逻辑
}✅ 第三步:命名与设计优化建议
- 将 addTo(Weight) 改名为 add(Weight):更符合 Java 命名惯例(如 StringBuilder.append()),且语义更准确——该方法修改的是调用者自身,而非参数。
- 考虑使 Weight 类不可变(immutable):即所有方法返回新实例而非修改自身。这能彻底避免状态污染,提升线程安全性与可测试性。例如 add() 应返回新 Weight,而非 void。
? 总结
封装不是“不让人看”,而是“不让人乱动”。真正的封装体现在:
立即学习“Java免费学习笔记(深入)”;
- 所有与重量相关的计算逻辑(单位转换、四则运算、比较)均由 Weight 类内部统一实现;
- Project 等使用者只需理解“重量能做什么”(如 add()、divideBy()、lessThan()),而无需知道“它怎么存、怎么算”;
- 任何内部结构变更(如改用 long 存储总盎司数)都可在 Weight 内部平滑完成,对外零影响。
这正是面向对象设计的精髓:用接口隔离变化,用职责凝聚逻辑。


















