基类、抽象类、接口对比
抽象类和接口有很多类似的地方,它们都属于模板,用于一些基类复用、或者对一些基类进行规范;
区别是,抽象类既有供子类共用的逻辑,也有只能规范、子类自己实现的部分;而接口,则完全只定规范,实现全由其实现类完成;
一、抽象类和基类对比
1、定义与特点
| 对比项 | 抽象类 | 非抽象类 |
|---|---|---|
| 定义 | 使用 abstract 关键字声明,可以包含抽象方法和具体实现 | 完全实现所有方法,可以直接实例化 |
| 实例化 | 不能直接实例化,必须通过子类继承并实现抽象方法 | 可以直接实例化(除非是工具类等设计为不可实例化的类) |
| 方法 | 可以包含抽象方法(无实现)和具体方法(有实现) | 所有方法必须有具体实现(不能有抽象方法) |
2、设计目的
| 抽象类 | 非抽象类 |
|---|---|
| 作为基类,定义部分通用逻辑,强制子类实现特定行为(通过抽象方法)。 - 适合模板方法模式,提供骨架流程。 - 体现"是什么"(is-a)关系,强调继承的层次性 | 提供完整功能,直接使用。 - 适合作为最终实现或独立组件。 - 强调"能做什么"(行为的具体实现) |
3、抽象类及其使用的示例
bash
abstract class Animal {
//抽象方法(无实现)
abstract void makeSound();
// 具体方法(有实现)
void eat() {
System.out.println("Eating...");
}
}
class Dog extends Animal {
@Override
void makeSound() {
System.out.println("Bark!");
}
}
// 使用
Animal dog = new Dog(); // 通过子类实例化
dog.makeSound(); // 输出: Bark!
dog.eat(); // 输出: Eating...
3、何时使用?
选择抽象类:
- 需要定义公共逻辑,但部分行为由子类决定。
- 强制子类遵循某种规范(如抽象方法)。
- 例如:InputStream 是抽象类,子类需实现 read()。
选择非抽象类:
- 功能完整且无需扩展。
- 不需要强制子类实现任何方法。
- 例如:String、ArrayList 等工具类。
二、抽象类和接口对比
1、相似之处
| 特性 | 抽象类 | 接口 |
|---|---|---|
| 不能直接实例化 | 必须被子类继承 | 必须被类实现 |
| 可以定义抽象方法 | 可以有抽象方法(abstract void foo();) | 所有方法默认是抽象的(Java 8 之前) |
| 支持多态 | 通过子类实现多态 | 通过实现类实现多态 |
| 可以用于代码复用 | 提供部分实现 | Java 8 后支持默认方法(default) |
todo:了解接口的默认方法使用场景
2、核心区别
| 对比项 | 抽象类 | 非抽象类 |
|---|---|---|
| 关键字 | abstract class | interface |
| 继承/实现方式 | 单继承(Java 是单继承) | 多实现(一个类可实现多个接口) |
| 变量 | 可以包含实例变量(private, protected, public) | 只能包含常量(public static final,默认) |
| 构造方法 | 可以有构造方法(用于子类初始化) | 不能有构造方法 |
| 方法实现 | 可以包含具体方法(有实现)和抽象方法 | Java 8 前所有方法都是抽象的,Java 8+ 支持 default 和 static 方法 |
| 设计目的 | 部分实现 + 强制子类实现某些方法(模板模式) | 定义行为规范(多态、解耦) |
| 使用场景 | 适用于is-a关系(如 Animal 是 Dog 的父类) | 适用于can-do关系(如 Runnable 表示可运行的任务) |
3、如何选择
使用抽象类的情况:
- 多个子类有公共逻辑,但部分方法需要子类自定义(如 AbstractList 提供部分实现,ArrayList 继承它)。
- 需要维护状态(成员变量),而不仅仅是方法定义。
- 希望强制子类实现某些方法(如 Animal 的 makeSound() 必须由子类实现)。
使用接口的情况:
-
定义行为契约(如 Comparable 表示可比较,Runnable 表示可运行)。
-
需要多继承(Java 不支持多继承类,但支持多实现接口)。
-
未来可能扩展(接口更灵活,Java 8+ 支持默认方法,不影响已有实现类)。
三、抽象类的经典使用场景------把一些类的父类定为为抽象类
为什么有些父类推荐定义为抽象类?有什么好处?
1、强制子类实现特定行为(规范约束)
抽象类可以定义抽象方法(没有实现的方法),要求所有子类必须实现这些方法。
- 作用:确保子类遵循统一的规范,避免遗漏关键逻辑。
2、定义部分实现的模板(和第一个是同一种东西,但是它有2个用处)
- 抽象类可以定义算法骨架,部分步骤由子类实现。
- 作用:控制流程结构,同时允许子类灵活扩展。
和1是同一种内容,就是父类可以定义抽象方法,子类必须实现,父类定个规范即可,调用时会自动执行子类重写的方法;
但是这个东西,它有1、2两个作用。
3、限制直接实例化(设计安全性)
- 抽象类不能直接实例化,必须通过子类使用。
- 作用:防止误用不完整的父类,确保功能由具体子类实现。
4、代码复用(这个不是抽象类作为父类时特有的,是说抽象类也有普通类作为父类时的用处)
- 抽象类可以包含具体方法(已实现的方法),子类直接复用这些逻辑,无需重复编写。
- 作用:减少重复代码,提高可维护性。