教程中心 / Java教程 / 抽象类和接口

抽象类和接口

20分钟 面向对象

引言:为什么需要抽象?

在面向对象设计中,我们经常需要定义一些"只给出方法声明,不给出具体实现"的类。这就是抽象的概念。

抽象类比

抽象就像建筑图纸:

图纸(抽象类/接口):
- 规定房子必须有"开门方法"、"关门方法"
- 但不规定具体怎么开门、怎么关门

具体建筑(实现类):
- 必须按照图纸实现开门、关门
- 但具体怎么开门、关门由建筑商决定

抽象类

什么是抽象类?

抽象类是用abstract关键字修饰的类,不能直接实例化,只能被继承。

抽象方法

抽象方法是没有方法体的方法,子类必须实现:

public abstract class Shape {
    protected String color;

    public Shape(String color) {
        this.color = color;
    }

    public abstract double getArea();

    public abstract double getPerimeter();

    public void printColor() {
        System.out.println("颜色: " + color);
    }
}

抽象类的特点

使用abstract关键字修饰
不能直接实例化(不能new)
可以包含抽象方法和具体方法
子类继承抽象类后,必须实现所有抽象方法
可以有构造方法(供子类调用)
可以包含成员变量

抽象类示例

abstract class Animal {
    protected String name;

    public Animal(String name) {
        this.name = name;
    }

    public abstract void sound();

    public abstract void eat();

    public void sleep() {
        System.out.println(name + "正在睡觉");
    }
}

class Dog extends Animal {
    public Dog(String name) {
        super(name);
    }

    @Override
    public void sound() {
        System.out.println(name + "汪汪汪");
    }

    @Override
    public void eat() {
        System.out.println(name + "正在吃狗粮");
    }
}

接口

什么是接口?

接口是一种特殊的抽象类型,完全抽象,只声明方法,不提供实现(Java 8之前)。

接口定义

public interface Drawable {
    int MAX_SIZE = 100;

    void draw();

    void move(int x, int y);
}

接口实现

public class Circle implements Drawable {
    private int x, y;
    private double radius;

    public Circle(int x, int y, double radius) {
        this.x = x;
        this.y = y;
        this.radius = radius;
    }

    @Override
    public void draw() {
        System.out.println("画一个圆,圆心(" + x + "," + y + "),半径" + radius);
    }

    @Override
    public void move(int x, int y) {
        this.x = x;
        this.y = y;
        System.out.println("移动到(" + x + "," + y + ")");
    }
}

接口的特性

接口完全抽象,所有方法都是抽象的(Java 8之前)
接口变量自动public static final(常量)
接口方法自动public abstract
类实现接口使用implements关键字
一个类可以实现多个接口
接口不能实例化
接口没有构造方法

Java 8的接口新特性

默认方法(Default Methods)

接口可以包含带默认实现的方法:

public interface Drawable {
    void draw();

    default void print() {
        System.out.println("打印图形");
    }

    static void help() {
        System.out.println("这是一个Drawable接口");
    }
}

静态方法(Static Methods)

public interface MathUtil {
    static int add(int a, int b) {
        return a + b;
    }

    static int multiply(int a, int b) {
        return a * b;
    }
}

int result = MathUtil.add(3, 5);  // 调用静态方法

抽象类 vs 接口

特性抽象类接口
继承单继承(只能extends一个)多实现(可以implements多个)
属性任意类型只能是常量(public static final)
方法抽象方法 + 具体方法抽象方法 + 默认方法 + 静态方法
构造方法可以有不能有
成员变量可以有只能是常量
使用场景"是什么"(is-a)"能做什么"(can-do)

何时使用抽象类?

  • 需要共享代码和状态(成员变量)
  • 类之间有"是"的关系(Dog is Animal)
  • 需要非public的成员

何时使用接口?

  • 需要定义行为规范
  • 类之间有"能"的关系(Bird can Fly)
  • 需要多继承的场景

多接口实现

一个类可以实现多个接口:

interface Walkable {
    void walk();
}

interface Runnable {
    void run();
}

interface Swimmable {
    void swim();
}

class Duck implements Walkable, Runnable, Swimmable {
    @Override
    public void walk() {
        System.out.println("鸭子走路");
    }

    @Override
    public void run() {
        System.out.println("鸭子跑步");
    }

    @Override
    public void swim() {
        System.out.println("鸭子游泳");
    }
}

接口继承

接口可以继承其他接口:

interface Animal {
    void eat();
}

interface Pet extends Animal {
    void play();
}

class Dog implements Pet {
    @Override
    public void eat() {
        System.out.println("狗吃东西");
    }

    @Override
    public void play() {
        System.out.println("狗玩耍");
    }
}

实战练习

练习:设计文件系统

interface FileSystemItem {
    String getName();
    long getSize();
    default void print() {
        System.out.println(getName() + " (" + getSize() + " bytes)");
    }
}

class File implements FileSystemItem {
    private String name;
    private long size;

    public File(String name, long size) {
        this.name = name;
        this.size = size;
    }

    @Override
    public String getName() { return name; }

    @Override
    public long getSize() { return size; }
}

class Folder implements FileSystemItem {
    private String name;
    private List items = new ArrayList<>();

    public Folder(String name) {
        this.name = name;
    }

    public void addItem(FileSystemItem item) {
        items.add(item);
    }

    @Override
    public String getName() { return name; }

    @Override
    public long getSize() {
        return items.stream().mapToLong(FileSystemItem::getSize).sum();
    }

    @Override
    public void print() {
        System.out.println("文件夹: " + name);
        for (FileSystemItem item : items) {
            item.print();
        }
    }
}

小结

  • 抽象类:用abstract修饰,不能实例化,需继承
  • 接口:完全抽象,多个接口用逗号分隔实现
  • 抽象类单继承,接口多实现
  • 抽象类用于"是"的关系,接口用于"能"的关系
  • 接口默认方法(Java 8)提供带实现的可选方法
  • Java 8+接口可以包含默认方法、静态方法
☕ Java 在线代码编辑器
📝 运行结果