抽象类、普通类和接口是面向对象编程中三个核心概念,它们在设计模式、代码复用和扩展性上有不同的作用。下面用详细的对比和示例来讲解它们的区别。
1. 普通类(Concrete Class)
- 定义:普通类是具体实现所有方法的类,可以直接实例化对象。
- 核心特点:
- 完全实现的方法:所有方法都有具体实现。
- 直接实例化:可以通过
new
关键字创建对象。 - 可继承性:可以作为父类被其他类继承,或独立使用。
- 适用场景:定义具体的对象或行为(如
String
、ArrayList
)。
示例:
// 普通类:可以直接实例化
class Dog {
private String name;
public Dog(String name) {
this.name = name;
}
// 具体方法
public void bark() {
System.out.println(name + " 汪汪叫!");
}
}
// 使用
Dog myDog = new Dog("小黑");
myDog.bark(); // 输出:小黑 汪汪叫!
2. 抽象类(Abstract Class)
- 定义:用
abstract
声明的类,不能直接实例化,可以包含抽象方法(无实现)和具体方法(有实现)。 - 核心特点:
- 不可实例化:必须通过子类继承并实现抽象方法后才能使用。
- 抽象方法:用
abstract
声明的方法,没有方法体(如void run();
)。 - 混合实现:可以同时包含抽象方法和具体方法。
- 构造方法:可以有构造方法(供子类初始化时调用)。
- 适用场景:定义公共代码(如模板方法模式)和强制子类实现特定行为。
示例:
// 抽象类:不能直接实例化
abstract class Animal {
protected String name;
public Animal(String name) {
this.name = name;
}
// 抽象方法(子类必须实现)
public abstract void makeSound();
// 具体方法(子类可以直接继承)
public void sleep() {
System.out.println(name + " 正在睡觉...");
}
}
// 子类必须实现抽象方法
class Cat extends Animal {
public Cat(String name) {
super(name);
}
@Override
public void makeSound() {
System.out.println(name + " 喵喵叫!");
}
}
// 使用
Animal myCat = new Cat("小白");
myCat.makeSound(); // 输出:小白 喵喵叫!
myCat.sleep(); // 输出:小白 正在睡觉...
3. 接口(Interface)
- 定义:用
interface
声明的类型,完全抽象(Java 8 前),定义一组方法规范,不包含具体实现。 - 核心特点:
- 完全抽象(默认):所有方法默认是
public abstract
的(Java 8 前)。 - 默认方法:Java 8 后允许用
default
定义具体方法。 - 静态方法:Java 8 后允许定义静态方法。
- 多继承:一个类可以实现多个接口。
- 无状态:不能包含实例字段(只有
public static final
常量)。
- 完全抽象(默认):所有方法默认是
- 适用场景:定义行为规范(如
Runnable
、Comparable
)。
示例:
// 接口:定义行为规范
interface Flyable {
// 抽象方法(默认 public abstract)
void fly();
// 默认方法(Java 8+)
default void land() {
System.out.println("正在着陆...");
}
// 静态方法(Java 8+)
static void checkFuel() {
System.out.println("燃料检查完成!");
}
}
// 实现接口
class Bird implements Flyable {
@Override
public void fly() {
System.out.println("鸟儿在飞翔!");
}
}
// 使用
Bird sparrow = new Bird();
sparrow.fly(); // 输出:鸟儿在飞翔!
sparrow.land(); // 输出:正在着陆...
Flyable.checkFuel(); // 输出:燃料检查完成!
三者的核心区别对比表
特性 | 普通类 | 抽象类 | 接口(Java 8+) |
---|---|---|---|
关键字 | 无 | abstract |
interface |
实例化 | 可以直接实例化 | 不能直接实例化 | 不能直接实例化 |
方法实现 | 所有方法必须实现 | 可以包含抽象方法和具体方法 | 默认只有抽象方法,但支持 default 和 static 方法 |
字段 | 可以有实例字段 | 可以有实例字段 | 只能有 public static final 常量 |
构造方法 | 有 | 有 | 无 |
继承/实现 | 单继承(extends) | 单继承(extends) | 多实现(implements) |
设计目的 | 具体实现 | 定义部分规范 + 公共代码 | 定义纯行为规范 |
关键使用场景总结
普通类:
- 需要直接实例化对象。
- 所有方法都有具体实现。
- 例如:工具类(
StringUtils
)、实体类(User
)。
抽象类:
- 需要定义公共代码,同时强制子类实现某些方法。
- 适合模板方法模式(定义算法骨架)。
- 例如:
InputStream
(定义读取流程,子类实现具体细节)。
接口:
- 定义行为规范,不关心具体实现。
- 需要实现多继承(如一个类同时实现
Runnable
和Serializable
)。 - 例如:
Comparable
(定义比较规则)、List
(定义集合操作)。
如何选择?
优先用接口:
- 需要多继承。
- 定义纯行为规范(如
Runnable
的run()
)。 - 例如:微服务中的 API 定义。
用抽象类:
- 需要复用代码(如公共方法)。
- 需要部分方法由子类实现。
- 例如:
AbstractList
提供集合的通用实现,子类只需实现get()
和size()
。
用普通类:
- 所有方法都已明确实现。
- 不需要被扩展(用
final
修饰防止继承)。
常见误区
抽象类 vs 接口:
- 抽象类可以包含字段和构造方法,接口不能。
- 接口可以实现多继承,抽象类只能单继承。
Java 8 后的接口:
- 接口的
default
方法是为了向后兼容,避免破坏现有实现。 - 如果多个接口有相同的
default
方法,实现类必须重写该方法。
- 接口的
抽象类可以有 main 方法:
- 抽象类可以有
main
方法,但不能实例化自身(只能通过子类)。
- 抽象类可以有
通过合理使用抽象类、普通类和接口,可以设计出高内聚、低耦合、易于扩展的代码结构。