抽象类
约 1434 字大约 5 分钟
2026-03-27
一、抽象类的概念
在 Java 中,使用 abstract 修饰的类称为抽象类。抽象类不能直接创建对象,通常用于描述一组子类的共性,把“公共属性”和“公共行为”抽取到父类中。
抽象类的核心价值有两个:
- 复用公共代码,减少重复实现
- 约束子类必须实现某些方法,统一行为规范
二、抽象类的特点
2.1 基本规则
- 抽象类使用
abstract修饰 - 抽象类不能被
new - 抽象类中可以有普通方法、成员变量、构造器
- 抽象类中可以包含抽象方法
- 如果子类继承了抽象类,就必须实现其中所有抽象方法,否则子类也要声明为抽象类
2.2 抽象方法
抽象方法只有方法声明,没有方法体,也必须使用 abstract 修饰。
abstract class Animal {
public abstract void eat();
}注意:
- 抽象方法不能有方法体
- 抽象方法不能用
private、final、static修饰
原因很简单:
private方法不能被子类重写final方法禁止重写static方法属于类,不属于对象多态调用
三、抽象类的定义与使用
abstract class Animal {
private String name;
public Animal(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void sleep() {
System.out.println(name + " 正在睡觉");
}
public abstract void makeSound();
}
class Dog extends Animal {
public Dog(String name) {
super(name);
}
@Override
public void makeSound() {
System.out.println(getName() + ":汪汪汪");
}
}
public class Demo {
public static void main(String[] args) {
Animal animal = new Dog("小黑");
animal.makeSound();
animal.sleep();
}
}运行结果体现了两个点:
Animal不能直接实例化- 父类引用可以指向子类对象,体现多态
四、抽象类为什么可以有构造器
抽象类虽然不能直接创建对象,但它仍然可以有构造器。因为子类在创建对象时,一定会先调用父类构造器完成公共成员的初始化。
abstract class Person {
private String name;
public Person(String name) {
this.name = name;
}
public String getName() {
return name;
}
}
class Student extends Person {
public Student(String name) {
super(name);
}
}这里 Person 的构造器就是为了给所有子类复用初始化逻辑。
五、抽象类的典型应用场景
5.1 提取公共属性和公共方法
多个子类存在相同字段和相同行为时,可以放入抽象父类中统一维护。
例如:
- 员工类中都有姓名、工号
- 图形类中都有颜色、面积计算入口
- 支付类中都有校验、日志记录等公共步骤
5.2 规定子类必须完成的能力
父类只定义“做什么”,子类去决定“怎么做”。
abstract class Shape {
public abstract double area();
}
class Circle extends Shape {
private double radius;
public Circle(double radius) {
this.radius = radius;
}
@Override
public double area() {
return Math.PI * radius * radius;
}
}六、模板方法模式
抽象类最常见的设计模式之一是模板方法模式:把流程骨架写在父类中,把可变步骤交给子类实现。
abstract class DataParser {
public final void parse() {
readData();
processData();
saveData();
}
protected abstract void readData();
protected abstract void processData();
protected void saveData() {
System.out.println("保存处理结果");
}
}
class CsvParser extends DataParser {
@Override
protected void readData() {
System.out.println("读取 CSV 数据");
}
@Override
protected void processData() {
System.out.println("处理 CSV 数据");
}
}父类负责稳定流程,子类负责可变实现,这种模式在框架源码中非常常见。
七、抽象类与普通类、接口的区别
7.1 与普通类的区别
- 普通类可以直接实例化,抽象类不可以
- 普通类中的方法通常都有实现,抽象类中可以存在没有实现的方法
- 抽象类更强调“统一规范 + 代码复用”
7.2 与接口的区别
| 对比项 | 抽象类 | 接口 |
|---|---|---|
| 继承/实现方式 | extends | implements |
| 是否可有构造器 | 可以 | 不可以 |
| 是否可有成员变量 | 可以有普通成员变量 | 只能有常量 |
| 是否可有方法实现 | 可以 | 可以有 default、static 方法 |
| 一个类可继承/实现几个 | 只能继承一个抽象类 | 可以实现多个接口 |
简单理解:
- 抽象类强调“是什么”,适合提取共性
- 接口强调“能做什么”,适合定义能力
八、使用抽象类时的注意点
8.1 不能直接实例化
// 错误写法
// Animal animal = new Animal();8.2 子类必须实现抽象方法
如果漏掉实现,编译器会直接报错。
8.3 不要把所有父类都设计成抽象类
如果父类没有抽象行为,只是单纯封装公共逻辑,使用普通类即可。不要为了“看起来高级”而滥用抽象类。
8.4 抽象类更适合描述一类事物
例如:
AnimalEmployeeVehicle
这类对象天然有共同属性和共同流程,适合抽象成父类。
九、面试常问问题
9.1 抽象类可以没有抽象方法吗
可以。一个类即使没有抽象方法,也可以被声明为抽象类,目的是不允许外部直接创建对象。
9.2 抽象类中可以有 main 方法吗
可以。抽象类不能创建对象,但类本身依然可以被加载,静态成员依然可以使用。
9.3 抽象类和接口怎么选
优先这样判断:
- 需要复用代码、共享状态时,用抽象类
- 需要定义行为规范、支持多实现时,用接口
很多实际项目中,两者会配合使用,而不是二选一。
十、实践建议
- 先判断是否真的存在稳定的“父类共性”
- 抽象类中的公共逻辑要足够稳定,避免频繁修改影响所有子类
- 不要让抽象类过于庞大,避免父类承担过多职责
- 如果只是定义行为约束,优先考虑接口
