1. 定义与基本语法

抽象类

  • 使用abstract关键字声明
  • 可以包含抽象方法(无实现)和具体方法(有实现)
  • 可以有成员变量、构造方法
  • 子类通过extends关键字继承抽象类,必须实现所有抽象方法

接口

  • 使用interface关键字声明
  • Java 8 之前只能包含抽象方法和常量;Java 8 及以后可以有默认方法(default)和静态方法(static
  • 成员变量默认是public static final(常量)
  • 类通过implements关键字实现接口,必须实现所有抽象方法

2. 核心区别对比

特性 抽象类 接口
继承方式 单继承(一个类只能继承一个抽象类) 多实现(一个类可以实现多个接口)
方法类型 可以包含抽象方法和具体方法 Java 8 后有默认方法和静态方法
成员变量 可以有各种类型的成员变量 只能是public static final常量
构造方法 有构造方法(供子类调用) 没有构造方法
访问修饰符 可以使用privateprotectedpublic 方法默认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传递给父类的构造方法。

DogCat作为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 + 的接口默认方法使得接口可以提供默认实现,但不会改变接口作为行为规范的本质

更多推荐