Z
ZHANK
面向对象基础

抽象类与接口

掌握 abstract、interface、default 方法、函数式接口

抽象类与接口

抽象类和接口都是用来"定义规范"的——它们告诉子类"你必须做什么",但不规定"怎么做"。区别在于:抽象类描述"是什么"(is-a),接口描述"能做什么"(can-do)。

学完本章你将: 掌握 abstract 类、interface 接口、default 方法、选择策略。


抽象类 —— 不完整的类

如果父类的方法无法给出合理实现(比如"每个图形的面积算法不同"),就该用 abstract 声明为抽象方法。有抽象方法的类必须是抽象类,抽象类不能 new

java
public abstract class Shape {      // abstract 类
    protected String color;

    public Shape(String color) {   // 抽象类可以有构造方法
        this.color = color;
    }

    public abstract double area(); // 抽象方法——没有方法体,子类必须实现

    public void display() {        // 普通方法——子类直接继承
        System.out.println(color + " 图形,面积:" + area());
    }
}

public class Circle extends Shape {
    private double radius;

    public Circle(String color, double radius) {
        super(color);             // 调用抽象类的构造方法
        this.radius = radius;
    }

    @Override
    public double area() {        // 必须实现抽象方法
        return Math.PI * radius * radius;
    }
}

什么时候用抽象类? 多个类有共同的属性和方法,但又没法完全通用的场景。


接口 —— 行为契约

接口是一组方法的"合同"——实现接口就是签了合同,承诺一定提供这些方法。重点是**"能做什么"而不是"是什么"**。一个类可以实现多个接口,弥补了 Java 单继承的限制。

java
// 接口定义"能飞"和"能游泳"
public interface Flyable {
    void fly();  // 默认 public abstract
}

public interface Swimmable {
    void swim();
}

// Duck 既会飞又会游泳——同时实现两个契约
public class Duck implements Flyable, Swimmable {
    @Override
    public void fly() { System.out.println("鸭子飞行中"); }
    @Override
    public void swim() { System.out.println("鸭子游泳中"); }
}

default 方法 —— 接口也能有实现

Java 8 开始接口可以有 default 方法。目的是:不破坏已有实现类的前提下给接口增加新方法。

java
public interface Logger {
    // 旧方法——子类必须实现
    void log(String msg);

    // 新加的 default 方法——子类不想重写也能直接用
    default void logWithTimestamp(String msg) {
        log("[" + java.time.LocalTime.now() + "] " + msg);
    }
}

怎么选?一个判断标准

问自己一个问题:这些类本质上是同一种东西吗?

判断用抽象类用接口
有共享的成员变量❌(只能 static final)
有共享的构造逻辑
它们不是同一种东西,只是能做同一件事
需要多继承效果
Car 和 Bicycle 都是 Vehicle → 抽象类 Vehicle
Car 和 Plane 都能载客 → 接口 Carryable
Bird 和 Plane 都能飞 → 接口 Flyable