抽象类:定义「是什么」
有这样一种情况:你想定义一个类来表示「图形」,但你无法确定「图形」的具体行为——圆有面积计算方法,三角形也有,但它们的算法完全不同。
你能写出 Shape 类的 getArea() 方法吗?写不出来,但又想表达「所有图形都应该有面积」这个概念。
这时抽象类就派上用场了。
抽象类是什么
抽象类(Abstract Class)是不能直接实例化的类。它的主要作用是定义契约,让子类去实现具体行为。
java
public abstract class Shape {
// 抽象方法:没有实现,强制子类重写
public abstract double getArea();
// 普通方法:有默认实现,子类可选重写
public void print() {
System.out.println("面积:" + getArea());
}
}定义抽象类
java
public abstract class Animal {
protected String name;
// 抽象方法:没有方法体
public abstract void eat();
public abstract void sound();
// 普通方法:有实现
public void sleep() {
System.out.println(name + "在睡觉");
}
}abstract 关键字告诉编译器:「这个类不完整,不能直接 new」。
子类实现
继承抽象类的子类必须实现所有抽象方法(除非它自己也是抽象类):
java
public class Dog extends Animal {
public Dog(String name) {
this.name = name;
}
@Override
public void eat() {
System.out.println(name + "在吃狗粮");
}
@Override
public void sound() {
System.out.println(name + "汪汪叫");
}
}使用:
java
Animal animal = new Dog("旺财");
animal.eat(); // 旺财在吃狗粮
animal.sleep(); // 旺财在睡觉(继承自父类)抽象类的特点
1. 不能实例化
java
// ❌ 错误:抽象类不能 new
Animal animal = new Animal();
// ✅ 正确:创建子类对象
Animal animal = new Dog("旺财");2. 可以有构造方法
抽象类的构造方法不是为了给自己用,而是供子类初始化父类状态:
java
public abstract class Animal {
protected String name;
// 抽象类可以有构造方法
public Animal(String name) {
this.name = name;
}
}
public class Dog extends Animal {
public Dog(String name) {
super(name); // 调用父类构造
}
}3. 可以有普通方法
普通方法提供默认实现,子类可以直接使用,也可以重写:
java
public abstract class Animal {
protected String name;
// 有默认实现
public void breathe() {
System.out.println(name + "在呼吸");
}
// 没有默认实现
public abstract void eat();
}4. 单继承限制
抽象类仍然受 Java 单继承限制:
java
// ✅ 正确:多层继承
abstract class A { }
abstract class B extends A { } // B 继承 A
class C extends B { } // C 继承 B
// ❌ 错误:不能多继承
// abstract class C extends A, B { }模板方法模式
抽象类的经典应用是模板方法模式:父类定义算法骨架,具体步骤由子类实现。
java
public abstract class DataProcessor {
// 模板方法:final 防止被重写
public final void process() {
String raw = readData();
String result = processData(raw);
saveData(result);
}
// 抽象方法:子类必须实现
protected abstract String readData();
protected abstract String processData(String data);
// 普通方法:有默认实现
protected void saveData(String data) {
System.out.println("保存结果:" + data);
}
}
public class FileProcessor extends DataProcessor {
@Override
protected String readData() {
return "从文件读取的数据";
}
@Override
protected String processData(String data) {
return data.toUpperCase();
}
}调用 new FileProcessor().process() 会依次执行:读取 → 处理 → 保存。算法骨架不变,具体步骤由子类决定。
抽象类 vs 接口
| 对比项 | 抽象类 | 接口 |
|---|---|---|
| 关键字 | abstract class | interface |
| 继承 | 单继承 | 多实现 |
| 构造方法 | 可以有 | 不能有 |
| 方法 | 抽象 + 普通都可以 | JDK 7 前只能是抽象方法 |
| 字段 | 无限制 | 只能是常量 |
| 访问修饰符 | 任意 | 默认 public |
| 静态方法 | 可以有 | JDK 8+ 可以有 |
| 用途 | 「是什么」(is-a) | 「能做什么」(can-do) |
选型决策树
需要继承多个?
是 → 用接口
否 ↓
类之间有明确的"是"关系?
是 → 用抽象类
否 ↓
需要共享代码(普通方法)?
是 → 用抽象类
否 → 用接口实际应用:层级结构的抽象
java
// 抽象父类:定义通用属性和通用方法
public abstract class Employee {
protected String name;
protected double baseSalary;
public Employee(String name, double baseSalary) {
this.name = name;
this.baseSalary = baseSalary;
}
// 抽象方法:子类必须实现
public abstract double calculateSalary();
// 普通方法:子类共用
public void printInfo() {
System.out.println("姓名:" + name);
System.out.println("工资:" + calculateSalary());
}
}
// 正式员工
public class FullTimeEmployee extends Employee {
private double bonus;
public FullTimeEmployee(String name, double baseSalary, double bonus) {
super(name, baseSalary);
this.bonus = bonus;
}
@Override
public double calculateSalary() {
return baseSalary + bonus;
}
}
// 合同工
public class PartTimeEmployee extends Employee {
private int hours;
private double hourlyRate;
public PartTimeEmployee(String name, double baseSalary, int hours, double hourlyRate) {
super(name, baseSalary);
this.hours = hours;
this.hourlyRate = hourlyRate;
}
@Override
public double calculateSalary() {
return baseSalary + hours * hourlyRate;
}
}总结
抽象类 = 不完整的类
抽象方法 = 必须由子类实现的方法
abstract = 「我定义规矩,子类负责实现」抽象类是「是什么」的抽象——Animal、Shape、Employee——它们代表一类事物的共性,但每个具体事物又有自己的行为。抽象类为这类事物提供了共同的骨架,让代码复用和扩展成为可能。
