一、抽象类(Abstract Class)
抽象类是一种不能被实例化的类,主要用于作为其他类的基类(父类),用来抽取子类的通用特性和行为。
1.1 主要特点
-
定义方式:使用
abstract
关键字修饰类。 -
包含内容:
-
抽象方法:使用
abstract
关键字修饰,只有声明没有实现(没有方法体),必须由子类提供实现。 -
具体方法:可以有实现了的方法(非抽象方法),子类可以直接继承或重写。
-
成员变量:可以包含普通成员变量(实例变量)和静态变量,变量的访问修饰符可以是
public
,protected
,default
(包私有),但不能是private
(对于抽象方法)。 -
构造方法:可以有构造方法,用于初始化抽象类中定义的成员变量或供子类调用。但抽象类的构造方法不能直接用于实例化自身,只能由子类的构造方法调用。
-
初始化块:可以包含初始化块。
-
-
继承:类通过
extends
关键字继承抽象类。Java 是单继承,一个类只能直接继承一个抽象类(但可以多层继承)。
1.2 设计目的与使用场景
抽象类主要用于代码复用和表示 "is-a"(是一个) 的关系
。它适合为一组密切相关的类提供统一的模板或基类,这些类共享一些共同的属性和行为,但又有一些特定的行为需要各自实现。
典型使用场景:
-
模板方法模式:在抽象类中定义算法的骨架(一个最终的具体方法),并将一些步骤延迟到子类中实现。
-
家族式对象建模:为具有共同特征和行为的对象族定义基础类,如
Animal
抽象类被Dog
,Cat
等子类继承。 -
共享代码和状态:当多个子类需要共享一些通用的方法实现或成员变量时。
1.3 代码示例
// 抽象类示例:Animal
abstract class Animal {
// 成员变量
protected String name;
// 构造方法
public Animal(String name) {
this.name = name;
}
// 抽象方法(无方法体)
public abstract void makeSound();
// 具体方法(有方法体)
public void sleep() {
System.out.println(name + " is sleeping.");
}
}
// 具体子类:Dog
class Dog extends Animal {
public Dog(String name) {
super(name);
}
@Override
public void makeSound() {
System.out.println(name + " says: Woof!");
}
}
二、接口(Interface)
接口是一种完全抽象的类型,用于声明一组方法(行为契约),要求实现类必须提供这些方法的具体实现。
2.1 主要特点 (以Java 8为基准)
-
定义方式:使用
interface
关键字声明。 -
包含内容:
-
抽象方法:在 Java 8 之前,接口中的所有方法都是隐式
public abstract
的(无需显式写这两个关键字)。 -
默认方法 (Default Methods):Java 8 引入,使用
default
关键字修饰,提供默认实现。实现类可以重写,也可以直接继承。 -
静态方法 (Static Methods):Java 8 引入,使用
static
关键字修饰,可以通过接口名直接调用。 -
私有方法 (Private Methods):Java 9 引入,用于在接口内部封装代码,供默认方法或静态方法使用。
-
成员变量:接口中定义的变量默认为
public static final
的常量,必须显式初始化。 -
构造方法:接口不能有构造方法。
-
初始化块:接口不能包含初始化块。
-
-
实现与继承:
-
类使用
implements
关键字实现一个或多个接口(多重继承)。 -
接口使用
extends
关键字继承其他接口,并且可以同时继承多个接口。
-
2.2 设计目的与使用场景
接口主要用于定义行为契约(协议)和表示 "can-do"(能做什么) 或 "like-a"(像是一个) 的关系
。它关注于对象能做什么,而不关心对象是什么,实现了接口与实现的解耦。
典型使用场景
:
-
定义API契约:如定义支付网关、数据访问层等规范。
-
实现多态:让不相关的类能够表现出相同的行为。
-
回调机制:如事件监听器。
-
替代多重继承:Java 类可以通过实现多个接口来获得多种类型的行为。
2.3 代码示例
// 接口示例:Flyable
interface Flyable {
// 抽象方法 (隐式 public abstract)
void fly();
// 默认方法 (Java 8+)
default void takeOff() {
System.out.println("Preparing to fly...");
}
// 静态方法 (Java 8+)
static boolean isFlyingObject(Object obj) {
return obj instanceof Flyable;
}
// 常量 (隐式 public static final)
String UNIT = "meters";
}
// 实现类:Bird
class Bird implements Flyable {
@Override
public void fly() {
System.out.println("Bird is flying in the sky.");
}
}
// 另一个不相关的实现类:Airplane
class Airplane implements Flyable {
@Override
public void fly() {
System.out.println("Airplane is flying in the air.");
}
}
三、抽象类与接口的对比
为了让区别更直观,下面用一个表格来概括它们的核心差异
:
特性维度 |
抽象类 (Abstract Class) |
接口 (Interface) (Java 8+) |
---|---|---|
关键字 |
|
|
继承/实现 |
单继承 ( |
多实现 ( |
方法类型 |
抽象方法 + 具体方法 |
抽象方法 + 默认方法 ( |
成员变量 |
普通成员变量、静态变量 |
只能是 |
构造方法 |
有 |
无 |
初始化块 |
有 |
无 |
设计理念/关系 |
is-a (是一个),强调代码复用和分类 |
can-do/like-a (能做什么/像是一个),强调行为契约和解耦 |
访问修饰符(方法) |
抽象方法可以是 |
方法默认 |
主要用途 |
作为模板,提取同类公共特性,部分实现 |
定义行为规范,实现多重契约 |
版本兼容性 |
添加新方法可能需修改子类 |
Java 8+ 后可通过默认方法添加新功能而不破坏现有实现 |
四、如何选择:抽象类 vs 接口
选择使用抽象类还是接口,甚至结合使用,取决于你的具体设计需求
:
-
使用抽象类的情况:
- •
需要共享代码或状态(成员变量) among closely related classes.
- •
需要定义非公共的方法(
protected
或default
访问权限)。 - •
正在建模的类之间存在明显的 "is-a" 层次关系(如
Dog
is anAnimal
)。 - •
想通过模板方法模式定义算法的骨架。
- •
-
使用接口的情况:
- •
需要定义不相关类都能实现的行为契约。
- •
需要实现多重继承(多重行为类型)。
- •
希望实现与实现的解耦,更注重API的定义而非具体实现。
- •
想使用多态特性,让不同类型的对象对外提供相同的行为方式。
- •
-
结合使用:
一个类可以同时继承一个抽象类并实现多个接口,这常常能带来灵活而强大的设计。
// 结合使用示例 abstract class Vehicle { protected int speed; public abstract void start(); } interface ElectricPowered { void charge(); } class ElectricCar extends Vehicle implements ElectricPowered { @Override public void start() { System.out.println("Electric car starting..."); } @Override public void charge() { System.out.println("Charging the electric car..."); } }
核心选择原则:
-
关系类型:"是什么" -> 抽象类;"能做什么" -> 接口。
-
代码共享:需要 -> 抽象类;不需要 -> 接口。
-
状态共享:需要 -> 抽象类;不需要 -> 接口。
-
多重继承:需要 -> 接口;不需要 -> 两者皆可,但需进一步判断。
五、总结
-
抽象类更像是一个不完全的类,为相关类提供公共基础和模板,侧重于代码复用和内部状态管理。
-
接口更像是一个行为协议,定义了实现类应对外提供哪些服务,侧重于契约定义和解耦。
-
现代Java设计(尤其是Java 8引入默认方法后)更倾向于 "面向接口编程",优先考虑使用接口定义类型,以提高灵活性和可扩展性。抽象类则更适用于框架内部或存在明显继承关系的类族中,用于封装公共实现。
-
在设计时,组合(通过接口)优于继承是一个值得遵循的原则,它能降低耦合度。