抽象方法必须定义在抽象类中且无方法体,不能被private、static或final修饰;模板方法模式核心是final模板方法定义算法骨架,抽象钩子方法供子类定制。

抽象方法必须定义在抽象类里,且子类必须实现
Java 中的 abstract 方法不能有方法体,也不能被 private、static 或 final 修饰。它只声明行为契约,强制子类提供具体逻辑。
- 如果父类含
abstract方法,该类必须用abstract class声明,否则编译报错:error: abstract method in non-abstract class - 子类继承后,要么实现所有抽象方法,要么自己也声明为
abstract - 接口中也可以定义抽象方法(默认就是
public abstract),但模板方法模式通常依赖抽象类的继承链,所以优先用抽象类
模板方法模式的核心:final 模板方法 + 抽象钩子方法
模板方法模式不是靠“抽象方法”本身实现复用,而是靠一个 final 的非抽象方法(即模板方法)来定义算法骨架,再把可变步骤拆成 abstract 或 protected 钩子方法供子类定制。
abstract class DataProcessor {
// 模板方法:定义流程,不可重写
public final void process() {
loadData();
validateData();
transformData();
saveData();
}
// 这些是抽象钩子,由子类决定怎么执行
protected abstract void loadData();
protected abstract void validateData();
protected abstract void transformData();
protected abstract void saveData();
}
-
process()是final的——防止子类破坏整体流程顺序 - 四个
protected abstract方法是“空缺”,子类必须填;也可提供默认空实现(变成protected void xxx() {}),让子类选择性覆盖 - 注意访问修饰符:用
protected而非public,避免外部直接调用钩子方法破坏封装
常见错误:把模板方法也做成 abstract,或漏掉 final
有人误以为“模板方法”就该是抽象的,结果写出这样的代码:
abstract class BadTemplate {
// ❌ 错误:模板方法本身抽象,子类无法复用流程
public abstract void execute();
protected abstract void step1();
protected abstract void step2();
}
- 这样每个子类都要重写整个流程,完全失去“模板”的意义
- 另一类错误是忘了加
final,导致子类可能覆盖process(),绕过校验或跳过关键步骤 - 还有一种隐蔽问题:在模板方法里调用了
this.xxx(),而xxx()是子类重写的非抽象方法——此时会触发子类版本(多态),但若子类还没初始化完,可能引发NullPointerException
实际使用时,优先考虑组合优于继承
模板方法模式本质是基于继承的控制反转(IoC),但 Java 8+ 后更推荐用函数式接口 + 组合方式替代深度继承:
立即学习“Java免费学习笔记(深入)”;
class DataProcessor {
private final Supplier loader;
private final Predicate validator;
private final Function transformer;
private final Consumer saver;
public DataProcessor(Supplier loader,
Predicate validator,
Function transformer,
Consumer saver) {
this.loader = loader;
this.validator = validator;
this.transformer = transformer;
this.saver = saver;
}
public void process() {
Data data = loader.get();
if (validator.test(data)) {
saver.accept(transformer.apply(data));
}
}
}
- 这种方式更灵活,测试更容易(可传入 mock 函数),也不受单继承限制
- 抽象类模板适合“强生命周期约束”的场景(如框架回调、资源必须按序初始化/销毁)
- 真正需要模板方法的地方,往往是框架层(如 Spring 的
JdbcDaoSupport、JUnit 的TestCase)——业务代码里过度使用容易导致类爆炸和紧耦合
transform())会让子类难以复用;太细(比如拆成 beforeTransform()、doTransform()、afterTransform())又让子类负担过重。得根据变化频率和正交性来权衡。










