应优先用组合+接口替代继承,仅在“是一个”关系时使用继承;避免protected暴露状态,禁止构造器中调用可重写方法,依赖应面向接口而非具体类。

避免滥用继承导致的代码耦合,核心是回归继承的本质:它只应表达“是一个”(is-a)关系,而不是为了复用代码或组织功能。一旦把 extends 当作“能少写几行就用”的捷径,耦合和维护风险就会迅速放大。
优先用组合 + 接口,而非继承
当两个类之间是“有一个”(has-a)或“用一个”(uses-a)关系时,坚决不用继承。比如缓存数据库访问器、订单包含商品、汽车拥有引擎——这些都不是子类是父类的一种,而是协作关系。
- 定义清晰接口(如
Dao),让不同实现类(JdbcDao、CachedDao)各自实现 -
CachedDao内部持有一个Dao实例(private final Dao delegate),需要时委托调用 - 缓存逻辑完全隔离,不侵入数据访问契约;测试时可轻松注入 mock 实现
警惕 protected 暴露内部状态
protected 字段或方法常被误当作“留给子类扩展的入口”,实则是耦合温床。子类一旦直接操作父类的 protected List<String> items,就可能破坏父类封装和不变性。
当用户要求为 Java 代码添加注释、修改注释、审查注释、整理注释格式,或要求按照代码注释规范处理 Java 文件时,使用本技能。本技能用于统一生成和维护 Java 类注释模板、按 Git 对比 release 判断版本号、补充每行代码上方中文块注释、调整注释空行规则,并保持 Java 文件 UTF-8 无 BOM 编码。典型触发场景包括:补充类注释、更新 @version、检查 @author 来源、整理行尾注释、把注释放到代码上一行、检查注释是否符合规范、构建前检查 Java 注释风格。
- 所有字段尽量声明为
private,不暴露原始引用 - 如需子类参与行为,提供受控的
protected方法(如protected void onItemAdded(String item)),而非开放字段 - 若必须返回集合,返回不可变视图:
Collections.unmodifiableList(items)
禁止在构造器中调用可重写方法
父类构造过程中调用被子类重写的方法,是典型的“半初始化陷阱”。此时子类字段尚未赋值,极易触发 NullPointerException 或逻辑错乱。
立即学习“Java免费学习笔记(深入)”;
- 把初始化逻辑拆到
init()等显式方法中,由使用者控制调用时机 - 或将关键方法声明为
final,从语法上阻止子类重写 - 更彻底的做法:将整个父类声明为
final,如public final class StringUtils
用接口替代具体类依赖
代码中直接依赖 ArrayList 或 LinkedList 这类具体实现类,会把调用方牢牢绑定在特定结构上,失去替换灵活性。
- 参数、变量、返回类型优先使用接口(
List、Collection、Map) - 创建实例时才选用具体实现(
new ArrayList<>()),且尽量限制在工厂或配置层 - 这样未来切换实现(如改用
CopyOnWriteArrayList)只需改一处,不影响业务逻辑

















