extends 的核心是审慎设计“is-a”关系,而非单纯代码复用;应抽象共性本质、避免无关字段污染、优先组合替代继承、正确调用 super 初始化、禁止字段隐藏、配合 sealed 控制继承边界。

用 extends 建立清晰可维护的类链,关键不在“能不能继承”,而在于“是否该继承”以及“如何让父类字段和逻辑真正服务于子类行为”。它不是堆叠代码的捷径,而是有意识地组织“is-a”关系、控制变量生命周期、明确职责边界的工程实践。
紧扣“is-a”设计父类抽象层级
父类应代表一组共性本质,而非功能集合。比如“图形”比“绘图工具”更适合作为父类,因为 Circle、Rectangle 确实是图形;而“可导出”“可撤销”这类能力应抽离为接口,用 implements 实现。
- 避免把多个不相关的属性塞进一个父类(如在
Shape中加ownerName或createTime),这会污染继承语义 - 优先提取公共字段(如
color、filled)和通用方法(如describe()),但保留具体行为由子类实现 - 若某类只复用部分逻辑,考虑用组合代替继承——例如
Car拥有Engine,而不是继承Engine
用 super 正确初始化父类状态
子类实例中父类字段是独立持有的,但必须通过 super() 显式调用父类构造器来初始化,否则易出现 null 或默认值引发的逻辑错误。
- 子类构造器第一行必须是
super(...)(除非父类有无参构造器且你接受其默认初始化) - 不要绕过
super直接给this.color = ...赋值——看似可行,实则跳过了父类可能包含的校验或初始化逻辑 - 若父类字段是
protected,子类可直接访问;但若父类提供了setColor()方法,优先使用它,保持封装一致性
避免字段隐藏,保持继承链语义统一
子类声明与父类同名字段(如父类有 protected String name,子类又写 private String name),会导致访问歧义:调用 this.name 指向子类字段,super.name 才指向父类字段。这不是重写,是隐藏,极易引发 bug。
- 一律禁止同名字段定义;如需扩展含义,改用新字段名(如
displayName)或通过方法封装(如getFullName()) - 所有可被子类使用的字段,用
protected明确开放,而非靠public暴露细节 - 对必须限制访问的字段,坚持用
private+getter/setter,确保父类能控制变更逻辑
配合 sealed 和 permits 控制类链边界
当父类意图明确、行为稳定(如 Shape 只允许 Circle、Rectangle、Triangle 子类),就该用 Java 17+ 的 sealed 类机制收口继承范围。
- 声明
abstract sealed class Shape permits Circle, Rectangle, Triangle - 每个许可子类必须用
permits列表中指定,且自身需声明为final、sealed或non-sealed - 这样既保留了继承的结构优势,又防止意外扩展破坏设计契约,提升可维护性

















