【Java】抽象类和接口对比+详解
·
1. 定义与基本语法
抽象类:
- 使用
abstract关键字声明 - 可以包含抽象方法(无实现)和具体方法(有实现)
- 可以有成员变量、构造方法
- 子类通过
extends关键字继承抽象类,必须实现所有抽象方法
接口:
- 使用
interface关键字声明 - Java 8 之前只能包含抽象方法和常量;Java 8 及以后可以有默认方法(
default)和静态方法(static) - 成员变量默认是
public static final(常量) - 类通过
implements关键字实现接口,必须实现所有抽象方法
2. 核心区别对比
| 特性 | 抽象类 | 接口 |
|---|---|---|
| 继承方式 | 单继承(一个类只能继承一个抽象类) | 多实现(一个类可以实现多个接口) |
| 方法类型 | 可以包含抽象方法和具体方法 | Java 8 后有默认方法和静态方法 |
| 成员变量 | 可以有各种类型的成员变量 | 只能是public static final常量 |
| 构造方法 | 有构造方法(供子类调用) | 没有构造方法 |
| 访问修饰符 | 可以使用private、protected、public |
方法默认public,变量默认public static final |
| 使用场景 | 表示 "is-a" 关系,用于抽取类间的共性实现 | 表示 "has-a" 能力,用于定义类的行为规范 |
3. 示例代码
抽象类示例
// 抽象类 - 表示动物
abstract class Animal {
// 成员变量
protected String name;
// 构造方法
public Animal(String name) {
this.name = name;
}
// 抽象方法(无实现)
public abstract void makeSound();
// 具体方法(有实现)
public void eat() {
System.out.println(name + "正在吃东西");
}
}
// 子类继承抽象类
class Dog extends Animal {
public Dog(String name) {
super(name);
}
// 必须实现抽象方法
@Override
public void makeSound() {
System.out.println(name + "汪汪叫");
}
}
class Cat extends Animal {
public Cat(String name) {
super(name);
}
@Override
public void makeSound() {
System.out.println(name + "喵喵叫");
}
}
public class Test {
public static void main(String[] args) {
Animal dog = new Dog("阿黄");
Animal cat = new Cat("咪咪");
dog.makeSound();
dog.eat();
cat.makeSound();
cat.eat();
}
}
super(name)的作用是调用父类(Animal类)的构造方法,并将参数name传递给父类的构造方法。
Dog和Cat作为Animal的子类,它们的构造方法中必须通过super(name)显式调用父类的构造方法。这是因为父类Animal定义了带参数name的构造方法,而 Java 规定:当父类没有无参构造方法时,子类构造方法必须在第一行通过super(参数)调用父类对应的有参构造方法,以确保父类的成员变量(如name)能被正确初始化。
通过这种方式,子类创建对象时传入的name会被传递到父类,完成Animal类中name成员变量的赋值,使子类对象能继承并使用该属性(如在makeSound()和eat()方法中通过name输出动物名称)。
接口示例
// 接口 - 表示会飞的能力
interface Flyable {
// 常量
int MAX_HEIGHT = 1000; // 等价于 public static final int MAX_HEIGHT = 1000;
// 抽象方法
void fly();
// 默认方法(Java 8+)
default void land() {
System.out.println("降落到地面");
}
// 静态方法(Java 8+)
static void showMaxHeight() {
System.out.println("最大飞行高度: " + MAX_HEIGHT + "米");
}
}
// 接口 - 表示会游泳的能力
interface Swimmable {
void swim();
}
// 类实现多个接口
class Duck extends Animal implements Flyable, Swimmable {
public Duck(String name) {
super(name);
}
@Override
public void makeSound() {
System.out.println(name + "嘎嘎叫");
}
@Override
public void fly() {
System.out.println(name + "在天空飞翔");
}
@Override
public void swim() {
System.out.println(name + "在水里游泳");
}
}
4. 使用场景分析
使用抽象类的情况:
- 多个类共享相同的实现逻辑时(代码复用)
- 需要定义类的基础行为并为子类提供默认实现
- 需要包含成员变量和构造方法
- 表示类之间的继承关系("is-a")
使用接口的情况:
- 定义类的行为规范但不关心具体实现
- 需要实现多继承的效果(一个类具备多种能力)
- 作为不同模块之间的契约(API 设计)
- 表示类具备某种能力("has-a")
5. Java 8+ 接口新特性带来的变化
Java 8 引入的默认方法和静态方法模糊了抽象类和接口的界限,但核心区别依然存在:
- 接口不能有构造方法和普通成员变量
- 一个类只能继承一个抽象类,但可以实现多个接口
- 接口的默认方法可以被实现类重写,而抽象类的具体方法也可以被重写
6. 综合示例
// 抽象类 - 交通工具
abstract class Vehicle {
protected String brand;
protected int speed;
public Vehicle(String brand) {
this.brand = brand;
this.speed = 0;
}
// 抽象方法
public abstract void accelerate();
// 具体方法
public void brake() {
speed = Math.max(0, speed - 10);
System.out.println(brand + "减速,当前速度: " + speed + "km/h");
}
public void displayInfo() {
System.out.println("品牌: " + brand + ", 当前速度: " + speed + "km/h");
}
}
// 接口 - 可充电
interface Chargeable {
int MAX_BATTERY = 100; // 最大电量
void charge();
default void showBatteryLevel(int level) {
System.out.println("当前电量: " + level + "%");
}
}
// 接口 - 有导航
interface Navigable {
void navigate(String destination);
}
// 电动汽车 - 继承抽象类并实现多个接口
class ElectricCar extends Vehicle implements Chargeable, Navigable {
private int batteryLevel;
public ElectricCar(String brand) {
super(brand);
this.batteryLevel = 50; // 初始电量50%
}
@Override
public void accelerate() {
if (batteryLevel > 0) {
speed = Math.min(120, speed + 20);
batteryLevel = Math.max(0, batteryLevel - 5);
System.out.println(brand + "加速,当前速度: " + speed + "km/h");
showBatteryLevel(batteryLevel);
} else {
System.out.println("电量耗尽,无法加速");
}
}
@Override
public void charge() {
batteryLevel = Math.min(MAX_BATTERY, batteryLevel + 30);
System.out.println(brand + "正在充电");
showBatteryLevel(batteryLevel);
}
@Override
public void navigate(String destination) {
System.out.println("从当前位置导航到: " + destination);
System.out.println("预计行驶时间: 30分钟");
}
}
// 测试类
public class AbstractInterfaceDemo {
public static void main(String[] args) {
ElectricCar tesla = new ElectricCar("特斯拉Model 3");
tesla.displayInfo();
tesla.accelerate();
tesla.accelerate();
tesla.brake();
tesla.navigate("中央公园");
tesla.charge();
}
}

7. 总结
- 抽象类注重代码复用和继承关系,适合定义类的基础结构
- 接口注重行为规范和多实现,适合定义类的能力
- 抽象类是 "是什么",接口是 "能做什么"
- 在设计时,应优先考虑使用接口,当需要共享代码实现时再考虑抽象类
- Java 8 + 的接口默认方法使得接口可以提供默认实现,但不会改变接口作为行为规范的本质
更多推荐
所有评论(0)