抽象方法是只声明“做什么”而不实现“怎么做”的行为契约,必须定义在abstract类或接口中;子类若为具体类则必须全部实现,否则编译失败。

抽象方法是 Java 中最直接、最可靠的契约工具——它不靠文档提醒,不靠代码审查,而是由编译器在写完代码的那一刻就强制校验:子类若为具体类,就必须实现所有继承来的抽象方法,否则根本通不过编译。
抽象方法的本质是“行为契约”
它只声明“要做什么”,比如 parse()、validate()、calculateFee(),但不规定“怎么做”。这种留白不是缺陷,而是设计意图:支付渠道子类决定怎么调三方接口,审批子类决定怎么查组织架构,解析子类决定如何处理 JSON 或 XML——细节各异,但入口和语义必须统一。
关键点在于:只要父类中存在抽象方法,该类就必须声明为 abstract;而任何非 abstract 的子类,编译器会逐个检查是否覆盖了全部抽象方法。漏一个?编译失败,报错信息清晰明确:
- class WechatPay must either be declared abstract or implement abstract method calculateFee() in PaymentProcessor
配合模板方法,把流程骨架和可变环节分开
光有抽象方法只能卡住“有没有实现”,管不住“什么时候调、前后做什么”。真正落地时,应搭配 模板方法模式:
- 用 final 方法封装不变流程(如日志记录、参数校验、结果包装)
- 把核心差异点声明为 protected abstract 方法,留给子类填空
- 子类只需专注自己的逻辑,不用重复写预处理或后置动作
例如一个统一的消息发送抽象类:
public abstract class MessageSender {
public final void send(String content) {
preCheck(content); // 公共前置
doSend(content); // ← 子类必须实现
postLog(content); // 公共后置
}
protected abstract void doSend(String content);
}
避开常见实现陷阱
看似简单,但实际开发中容易因细节疏忽导致“看似重写了,实则没覆盖”:
- 参数类型必须完全一致——java.util.List 和自定义 List 是不同类型,不算重写
- 返回类型支持协变(子类可返回更具体的类型),但不能换成基本类型或无关类型
- 访问修饰符不能比父类更严格:父类 public abstract,子类不能写 protected
- 不能用 private / static / final 修饰抽象方法,它们与“被继承实现”逻辑冲突
为什么不用接口代替?
接口无法提供可复用的执行逻辑。如果你需要:
- 共享字段(如请求超时配置、重试次数)
- 带构造逻辑的初始化(如连接池预热、上下文注入)
- 非抽象方法的默认实现(如通用加密、签名生成)
- 运行时类型识别(instanceof AbstractService 可行,instanceof Interface 不行)
这些都只能靠抽象类支撑。接口适合定义“能做什么”,抽象类适合定义“怎么起步、哪些必须做、哪些可以自己定”。

















