在选择使用抽象类而非接口时,通常基于特定的设计需求、代码复用性和实现细节。以下是一些更具体的场景,这些场景更倾向于使用抽象类:
1. 需要代码复用
场景描述:
当多个类需要共享一些具体的代码实现时,抽象类是一个很好的选择。抽象类可以包含具体的方法实现,这些实现可以被其子类直接使用,而无需重写。
示例:
假设你正在开发一个游戏,其中有多种角色(如战士、法师等)。这些角色类可能都有一些共同的行为,比如移动、攻击等。此外,它们可能还需要一些共享的属性,比如生命值、魔法值等。
abstract class Character {
protected int health;
protected int mana;
public Character(int health, int mana) {
this.health = health;
this.mana = mana;
}
// 具体的移动方法实现
public void move() {
System.out.println("Character is moving.");
}
// 抽象的攻击方法,需要在子类中实现
public abstract void attack();
// 可能的共享方法,比如获取生命值
public int getHealth() {
return health;
}
// ... 其他共享方法和属性
}
class Warrior extends Character {
public Warrior(int health, int mana) {
super(health, mana);
}
@Override
public void attack() {
System.out.println("Warrior is attacking with sword.");
}
}
class Mage extends Character {
public Mage(int health, int mana) {
super(health, mana);
}
@Override
public void attack() {
System.out.println("Mage is attacking with magic.");
}
}
在这个例子中,Character
类是一个抽象类,它定义了生命值、魔法值等共享属性,并提供了移动方法的具体实现。攻击方法是一个抽象方法,需要在子类中实现。这样,Warrior
和 Mage
类就可以继承 Character
类,并复用其移动方法和共享属性。
2. 需要控制继承层次
场景描述:
在某些情况下,你可能希望限制哪些类可以继承特定的行为。使用抽象类可以帮助你实现这种控制,因为 Java 不支持多重类继承(即一个类不能同时继承多个类)。通过定义一个抽象类,你可以确保所有的子类都遵循相同的继承路径。
示例:
假设你正在开发一个汽车制造系统,其中有多种类型的汽车(如轿车、卡车等)。这些汽车类可能都有一些共同的行为,比如启动、停止等。此外,它们可能还需要一些共享的属性,比如速度、油量等。
abstract class Vehicle {
protected int speed;
protected int fuel;
public Vehicle(int speed, int fuel) {
this.speed = speed;
this.fuel = fuel;
}
// 具体的启动方法实现
public void start() {
System.out.println("Vehicle is starting.");
}
// 具体的停止方法实现
public void stop() {
System.out.println("Vehicle is stopping.");
}
// 抽象的方法,用于获取汽车类型,需要在子类中实现
public abstract String getType();
// ... 其他共享方法和属性
}
class Car extends Vehicle {
public Car(int speed, int fuel) {
super(speed, fuel);
}
@Override
public String getType() {
return "Car";
}
}
class Truck extends Vehicle {
public Truck(int speed, int fuel) {
super(speed, fuel);
}
@Override
public String getType() {
return "Truck";
}
}
在这个例子中,Vehicle
类是一个抽象类,它定义了速度、油量等共享属性,并提供了启动和停止方法的具体实现。获取汽车类型的方法是一个抽象方法,需要在子类中实现。这样,Car
和 Truck
类就可以继承 Vehicle
类,并复用其启动、停止方法和共享属性。同时,通过定义一个抽象类 Vehicle
,你可以确保所有的子类(如 Car
、Truck
等)都遵循相同的继承路径。
3. 需要访问修饰符和字段
场景描述:
抽象类允许你定义具有不同访问级别的成员(如 protected
或 private
),并提供字段来存储状态。这在接口中是不可能的,因为接口的成员默认是 public
的,且不能包含字段。
示例(延续上面的汽车制造系统示例):
abstract class Vehicle {
protected String manufacturer; // 受保护的成员变量
protected int speed;
protected int fuel;
public Vehicle(String manufacturer, int speed, int fuel) {
this.manufacturer = manufacturer;
this.speed = speed;
this.fuel = fuel;
}
// 具体的启动方法实现
public void start() {
System.out.println(manufacturer + " Vehicle is starting.");
}
// 具体的停止方法实现
public void stop() {
System.out.println(manufacturer + " Vehicle is stopping.");
}
// 抽象的方法,用于获取汽车类型,需要在子类中实现
public abstract String getType();
// 获取制造商的方法
public String getManufacturer() {
return manufacturer;
}
// ... 其他共享方法和属性
}
class Car extends Vehicle {
public Car(String manufacturer, int speed, int fuel) {
super(manufacturer, speed, fuel);
}
@Override
public String getType() {
return "Car";
}
}
class Truck extends Vehicle {
public Truck(String manufacturer, int speed, int fuel) {
super(manufacturer, speed, fuel);
}
@Override
public String getType() {
return "Truck";
}
}
在这个例子中,Vehicle
类有一个受保护的成员变量 manufacturer
,它存储了汽车制造商的信息。这个变量在子类中是可以访问的,因为子类继承了 Vehicle
类。此外,Vehicle
类还提供了启动和停止方法的具体实现,以及获取制造商信息的方法。这样,Car
和 Truck
类就可以继承 Vehicle
类,并复用其方法、属性和状态。