抽象类
概述
抽象类(Abstract Class)是用 abstract 修饰的类,用于表示不能直接实例化的「抽象概念」或「模板」。抽象类可以包含抽象方法(只有声明、没有方法体的方法),也可以包含普通方法、成员变量和构造方法。子类必须实现(重写)父类中的所有抽象方法才能被实例化,否则子类也必须声明为抽象类。抽象类常用于定义一类对象的共同行为骨架,把「必须由子类实现」的部分声明为抽象方法,把「可复用」的部分写成普通方法,从而在 继承 与 多态 中充当稳定的基类。
通俗理解:抽象类像是「半成品模板」——规定好了部分能力(普通方法)和必须由子类补全的「缺口」(抽象方法),本身不能直接 new,只能通过具体子类来用。
为什么需要抽象类
- 强制子类实现关键行为:把「必须由子类决定」的方法声明为抽象方法,编译器会检查子类是否全部实现,避免漏写。
- 复用公共逻辑:抽象类中的普通方法、成员变量可被所有子类共享,减少重复代码。
- 表达「是一种」的层次:抽象类表示「一类事物的抽象」,子类表示具体种类(如
Animal与Dog、Cat),便于面向抽象编程。 - 与接口的区别:抽象类可以有构造方法、实例字段和已实现的方法,适合作为「带状态的模板」;接口侧重「能力契约」,见 接口。
基本语法
声明抽象类
在 class 前加 abstract:
java
public abstract class Animal {
protected String name;
public Animal(String name) {
this.name = name;
}
// 抽象方法:无方法体,以分号结束;子类必须实现
public abstract void speak();
// 普通方法:子类可直接继承使用或重写
public void sleep() {
System.out.println(name + " is sleeping.");
}
}- 抽象类不能
new:new Animal("x")会编译错误。 - 抽象方法:用
abstract修饰,没有方法体(没有{ }),以分号结尾;子类必须提供实现。
子类实现抽象方法
子类必须实现父类中所有抽象方法,否则子类也必须声明为 abstract。
java
public class Dog extends Animal {
public Dog(String name) {
super(name); // 调用抽象类的构造方法
}
@Override
public void speak() {
System.out.println(name + " barks: Wang!");
}
}
// 此时 Dog 是具体类,可以实例化
Animal a = new Dog("Lucky");
a.speak(); // 多态:Lucky barks: Wang!
a.sleep(); // 使用抽象类中已实现的 sleep()注意
抽象类可以有构造方法。虽然抽象类本身不能实例化,但子类构造方法中会通过 super(...) 调用父类构造方法,用于初始化抽象类中定义的成员变量。
抽象类与普通类的区别
| 特性 | 普通类 | 抽象类 |
|---|---|---|
abstract | 无 | 必须有 |
| 实例化 | 可以 new | 不能直接 new |
| 抽象方法 | 不能有 | 可以有(0 个或多个) |
| 构造方法/字段 | 可以有 | 可以有 |
| 子类 | 可选继承 | 若继承则须实现抽象方法 |
使用示例
示例 1:抽象类作为模板(抽象方法 + 普通方法)
抽象类定义「模板方法」:一部分步骤已实现,关键步骤留给子类实现。
java
public abstract class DataParser {
// 抽象方法:子类必须实现「如何读取原始数据」
protected abstract String readRaw();
// 抽象方法:子类必须实现「如何解析」
protected abstract Object parse(String raw);
// 普通方法:模板流程,子类可直接使用
public final Object parseFromSource() {
String raw = readRaw();
return parse(raw);
}
}
public class JsonParser extends DataParser {
@Override
protected String readRaw() {
return "{\"name\":\"Java\"}"; // 示例:实际可从文件/网络读
}
@Override
protected Object parse(String raw) {
// 简化示例:实际可用 JSON 库解析
return raw;
}
}
DataParser p = new JsonParser();
Object result = p.parseFromSource(); // 多态:调用子类实现的 readRaw/parse示例 2:抽象类中的构造方法与字段
抽象类可以拥有构造方法、成员变量和已实现方法,子类通过 super() 初始化并复用。
java
public abstract class Shape {
private final String color;
public Shape(String color) {
this.color = color;
}
public String getColor() {
return color;
}
public abstract double area(); // 面积由子类实现
public abstract double perimeter(); // 周长由子类实现
// 已实现方法:所有子类共用
public void printInfo() {
System.out.println("Color: " + color + ", Area: " + area() + ", Perimeter: " + perimeter());
}
}
public class Rectangle extends Shape {
private final double width, height;
public Rectangle(String color, double width, double height) {
super(color); // 必须调用父类构造方法
this.width = width;
this.height = height;
}
@Override
public double area() {
return width * height;
}
@Override
public double perimeter() {
return 2 * (width + height);
}
}
Shape s = new Rectangle("red", 3, 4);
s.printInfo(); // Color: red, Area: 12.0, Perimeter: 14.0示例 3:抽象类与多态结合
用抽象类类型统一持有不同子类对象,体现多态。
java
public abstract class Animal {
protected String name;
public Animal(String name) { this.name = name; }
public abstract void speak();
}
public class Dog extends Animal {
public Dog(String name) { super(name); }
@Override
public void speak() { System.out.println(name + " barks."); }
}
public class Cat extends Animal {
public Cat(String name) { super(name); }
@Override
public void speak() { System.out.println(name + " meows."); }
}
Animal[] animals = { new Dog("Lucky"), new Cat("Mimi") };
for (Animal a : animals) {
a.speak(); // 多态:各自执行 Dog/Cat 的 speak
}抽象类的常见用法
- 定义模板:抽象类中写好流程(普通方法),关键步骤用抽象方法交给子类实现。
- 作为多态基类:变量/参数类型声明为抽象类,实际传入各种具体子类,统一处理。
- 部分实现:既有已实现方法(复用),又有抽象方法(强制子类实现),介于「完全实现」的普通类和「只定义契约」的接口之间。
注意事项
- 抽象类不能实例化:
new AbstractClass()会编译报错;只能通过具体子类实例化,再赋给抽象类引用(多态)。 - 抽象方法不能有方法体:抽象方法以分号结束,不能写
{ };实现必须在子类中完成。 - 子类必须实现全部抽象方法:否则子类也必须声明为
abstract。 - 抽象类可以有构造方法:供子类通过
super(...)调用,用于初始化抽象类中的字段。 - 抽象方法不能是 private、static 或 final:抽象方法需要子类重写,因此不能是 private;不能是 static(静态方法属于类,不参与重写);不能是 final(final 禁止重写)。
提示
若一个类中有抽象方法,该类必须声明为抽象类;但抽象类可以没有抽象方法(仅用 abstract class 表示「不允许实例化」)。
注意
抽象类与 接口 都能作为多态的类型;选择抽象类通常是因为需要共同状态(字段)或部分已实现逻辑,而接口更侧重「能力契约」、支持多实现。
相关链接
- 继承 — 抽象类作为父类,子类 extends 并实现抽象方法
- 多态 — 抽象类引用指向子类对象
- 接口 — 接口与抽象类的区别与选择
- Oracle Java 教程 - 抽象类