- 定义与语法细节
· 抽象类:使用 abstract class 定义,如 public abstract class Animal { }
· 抽象方法:使用 abstract 修饰,没有方法体(直接以分号结尾)。
public abstract void makeSound();
· 类中可包含:
· 抽象方法(强制子类实现)
· 非抽象方法(有具体实现)
· 成员变量(可以是 private、protected、public、static、final 等)
· 构造方法(虽然不能实例化,但供子类调用)
· 静态代码块、实例代码块
- 继承与实现规则
· 子类通过 extends 继承抽象类。
· 必须实现所有抽象方法,否则子类也必须声明为 abstract。
· 子类可以重写父类的非抽象方法(普通方法也可以被重写)。
· 一个子类只能继承一个抽象类(单继承)。
· 抽象类可以实现接口(使用 implements),且可以不实现接口中的方法(交由具体子类实现)。
- 构造方法与初始化
· 抽象类有构造方法,但不能直接 new。
· 子类构造方法中必须通过 super(...) 调用父类构造方法(隐式或显式)。
· 初始化顺序:父类静态块 → 子类静态块 → 父类实例块 → 父类构造方法 → 子类实例块 → 子类构造方法。
- 关键限制(与修饰符的互斥)
修饰符组合 是否允许 原因
abstract + final ❌ 方法/类均不行 final 禁止重写/继承,与抽象矛盾
abstract + private ❌ 方法 private 不能被子类访问,无法重写
abstract + static ❌ 方法 静态方法属于类,不能重写
abstract + native ❌ 方法 native 已有实现(非Java),与抽象冲突
- 与接口的细致对比(补充要点)
· 字段:接口字段默认 public static final;抽象类无此限制。
· 方法可见性:抽象类可以有 protected、private 方法;接口方法(Java 8前)只能是 public。
· 状态:抽象类可以维护可变状态(非 final 字段);接口通常无状态(除非定义 static final 常量)。
· Java 8+ 接口新特性:default 和 static 方法,但依然不能有实例字段。
· 选择原则:需要共享代码、状态 → 抽象类;定义能力(多实现) → 接口。
- 典型应用模式
· 模板方法模式
抽象类中定义骨架方法(final 防止篡改),调用一系列抽象方法或钩子方法。
```java
public abstract class DataParser {
public final void parse() {
readData();
processData();
writeData();
}
protected abstract void readData();
protected abstract void processData();
protected void writeData() { } // 钩子,默认空实现
}
```
· 工厂方法模式:抽象类定义创建对象的抽象方法,子类具体实现。
- 常见误区与面试题
· 误区:抽象类不能有构造方法 ❌ (实际可以有,用于初始化父类字段)
· 误区:抽象方法不能有主体,但可以有 {} ❌ (必须 ; 结尾,不能有大括号)
· 误区:abstract 类中只能有抽象方法 ❌ (可以有具体方法)
· 面试题:为什么抽象类可以 main 方法?
答:main 是静态方法,与实例化无关,可以正常执行,但需要子类或间接调用。
· 面试题:abstract 类能否实现接口且不实现方法?
答:可以,作为抽象类,它可以把接口方法继续声明为抽象方法,留给子类实现。
- 代码示例(完整演示)
```java
public abstract class Vehicle {
protected String brand;
public Vehicle(String brand) {
this.brand = brand;
}
public abstract void start(); // 抽象方法
public void showBrand() { // 具体方法
System.out.println("Brand: " + brand);
}
}
public class Car extends Vehicle {
public Car(String brand) {
super(brand);
}
@Override
public void start() {
System.out.println(brand + " car starts with key.");
}
}
// 使用
Vehicle v = new Car("Toyota");
v.start(); // Toyota car starts with key.
v.showBrand(); // Brand: Toyota
```