abstract类必须用abstract修饰且不可实例化;abstract方法也必须用abstract修饰、无方法体(不可写{}或return),只能在抽象类中声明,子类须全部实现或自身声明为abstract。

Java 中 abstract 类和 abstract 方法的声明语法
抽象类必须用 abstract 关键字修饰,且不能被实例化;抽象方法也必须用 abstract 修饰,不能有方法体(连大括号 {} 都不能写),且只能出现在抽象类中。
常见错误是给 abstract 方法写了空大括号或 return 语句,这会导致编译失败,报错:abstract methods cannot have a body。
- 抽象类可以包含普通方法、字段、构造器,但不能 new 实例
- 抽象方法不能是 private、static、final(这些修饰符与“强制子类实现”冲突)
- 子类继承抽象类时,必须实现所有 abstract 方法,否则子类也得声明为
abstract
abstract class OrderService {
protected String orderId;
// 普通方法,可直接调用
public void logOrder() {
System.out.println("Order: " + orderId);
}
// 抽象方法:无方法体,不写 {}
public abstract void validate();
public abstract boolean processPayment();
}
为什么 abstract 方法不能有方法体
抽象方法本质是契约——它只承诺“会有这个行为”,不承诺“怎么实现”。一旦加了 {},JVM 就认为你提供了实现,那就不是抽象的了,矛盾直接触发编译错误。
容易踩的坑包括:
- 误写成
public abstract void validate() {}→ 编译报错 - 在 abstract 方法里写
return null;或抛异常 → 同样非法,哪怕逻辑是“暂不支持”也不行 - 把接口方法和抽象方法混淆:接口中的
default或static方法可以有体,但 abstract 方法永远不行
抽象方法作为业务占位符的实际用途
在分层设计或模板方法模式中,抽象方法常用来预留扩展点。比如统一订单流程中,validate() 和 processPayment() 是各业务线差异最大的环节,父类用 abstract 占位,强制子类注入具体逻辑。
这种写法比空实现(return;)更严格:它让编译器帮你检查是否遗漏实现,避免运行时因未覆盖而抛 UnsupportedOperationException。
- 适合场景:框架基类、SDK 扩展点、多租户差异化逻辑入口
- 注意:抽象方法名要体现业务意图,比如
applyPromotionRule()比doSomething()更易维护 - 如果某子类确实无法实现某个抽象方法,说明类设计已违背 Liskov 替换原则,应重新审视继承关系
抽象类 vs 接口:选哪个做业务占位
当需要定义“有状态的通用行为骨架”时,用抽象类;当只约定“能力契约”且允许多实现时,优先用接口(Java 8+ 支持 default 方法补足灵活性)。
关键区别在于:抽象类能持有字段、提供部分实现、控制构造流程;接口不能有实例字段(只有 public static final 常量),也不能有构造器。
- 如果你的占位方法依赖类内字段(如
this.userId),只能放抽象类里 - 如果只是声明“谁都能做的动作”,比如
serialize()、retryPolicy(),接口更轻量、更解耦 - Java 不支持多继承,但一个类可实现多个接口——这点在复杂业务组合时影响很大
抽象方法本身没有性能开销,但继承链过深或滥用模板方法可能增加理解成本。真正难的是判断哪些逻辑该冻结在父类,哪些必须留给子类——这取决于业务稳定性和变化频率,不是语法问题。










