
在面向对象设计中,共用字段(如 id、description)应统一由父类声明并初始化;子类只需通过 super() 调用父类构造器传递共有参数,并专注定义自身特有属性与行为,无需重复声明继承字段或重写构造逻辑。
在面向对象设计中,共用字段(如 id、description)应统一由父类声明并初始化;子类只需通过 super() 调用父类构造器传递共有参数,并专注定义自身特有属性与行为,无需重复声明继承字段或重写构造逻辑。
当你设计广告管理系统时,Advertisement 作为基类承载所有广告共有的核心属性(如唯一标识 id 和描述 description),而 PrintedAd、WebpageAd 等子类仅需扩展其业务特异性字段(如页码成本、URL、播放时段等)。关键在于:父类负责封装通用状态,子类负责增强专属能力——而非复制粘贴字段与构造逻辑。
✅ 正确做法:利用继承 + super() 构造链
首先,将 Advertisement 声明为 abstract(因其本身不直接实例化),并使用 final 字段确保不可变性(推荐实践):
public abstract class Advertisement {
private final int id;
private final String description;
// 父类构造器:强制子类提供共用参数
public Advertisement(int id, String description) {
this.id = id;
this.description = Objects.requireNonNull(description, "description must not be null");
}
// 只读访问器
public int getId() { return id; }
public String getDescription() { return description; }
@Override
public String toString() {
return String.format("Advertisement{id=%d, description='%s'}", id, description);
}
}
接着,在子类构造器中显式调用 super(id, description),并将特有参数交由子类自身处理:
public class PrintedAd extends Advertisement {
private final int firstPageCost;
private final int middlePageCost;
private final int lastPageCost;
// 子类构造器:接收全部5个参数(2个共用 + 3个特有)
public PrintedAd(int id, String description,
int firstPageCost, int middlePageCost, int lastPageCost) {
super(id, description); // ← 关键:复用父类初始化逻辑
this.firstPageCost = firstPageCost;
this.middlePageCost = middlePageCost;
this.lastPageCost = lastPageCost;
}
// 特有 getter 方法
public int getFirstPageCost() { return firstPageCost; }
public int getMiddlePageCost() { return middlePageCost; }
public int getLastPageCost() { return lastPageCost; }
}
同理,WebpageAd 可定义为:
在 Java 中初始化和管理阿里云 SDK客户端。包括单例模式、线程安全、endpoint 与 region 配置、VPC 终端节点、同步与异步等。
public class WebpageAd extends Advertisement {
private final double costPerSecond;
private final int durationSeconds;
public WebpageAd(int id, String description,
double costPerSecond, int durationSeconds) {
super(id, description); // ← 复用父类字段初始化
this.costPerSecond = costPerSecond;
this.durationSeconds = durationSeconds;
}
// … getter 方法
}
⚠️ 常见错误与修正说明
❌ 错误:子类省略 super(...) 调用,导致 id 和 description 保持默认值(0 和 null)
→ 编译器会报错:若父类无无参构造器,子类必须显式调用 super(...)❌ 错误:在子类中重新声明 private int id; private String description;
→ 这会遮蔽(shadow)继承的字段,造成逻辑混乱与内存浪费;且破坏封装性-
✅ 最佳实践建议:
- 使用 final 字段 + 无 setter:保证对象不可变,提升线程安全与可预测性;
- 参数校验(如 Objects.requireNonNull)应在父类构造器中统一完成;
- 遵循 Java 命名规范:字段名小驼峰(id, description, firstPageCost),避免 ID 或 First;
- 若未来需支持动态扩展广告类型,可结合 sealed 类(Java 17+)限制继承范围,增强 API 稳定性。
? 总结:一次定义,处处复用
你无需、也不应为每个子类重复编写 id 和 description 字段及其赋值逻辑。真正的面向对象优势正在于此:
✅ 父类定义共性(字段 + 初始化契约)
✅ 子类专注差异(特有字段 + 业务逻辑)
✅ super() 是桥梁——它让子类在构造时自动获得父类状态,同时保持职责分离与代码简洁。
最终,Main.java 中的实例化变得清晰且类型安全:
public class Main {
public static void main(String[] args) {
Advertisement ad = new PrintedAd(34567, "Banner on front page", 150, 90, 120);
System.out.println(ad.getId()); // → 34567
System.out.println(ad); // → Advertisement{id=34567, description='Banner on front page'}
// 同样可安全向上转型
Advertisement webAd = new WebpageAd(34568, "Homepage banner", 0.02, 30);
}
}
这样,无论新增多少种广告类型(如 RadioAd、VideoAd),都只需继承 Advertisement 并在构造器中 super(id, desc) —— 共用字段永不重复,构造逻辑永不失控。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南










