抽象类、普通类和接口的区别详细讲解(面试题)

发布于:2025-05-25 ⋅ 阅读:(23) ⋅ 点赞:(0)

抽象类、普通类和接口是面向对象编程中三个核心概念,它们在设计模式、代码复用和扩展性上有不同的作用。下面用详细的对比和示例来讲解它们的区别。


1. 普通类(Concrete Class)

  • 定义:普通类是具体实现所有方法的类,可以直接实例化对象
  • 核心特点
    • 完全实现的方法:所有方法都有具体实现。
    • 直接实例化:可以通过 new 关键字创建对象。
    • 可继承性:可以作为父类被其他类继承,或独立使用。
  • 适用场景:定义具体的对象或行为(如 StringArrayList)。

示例

// 普通类:可以直接实例化
class Dog {
    private String name;

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

    // 具体方法
    public void bark() {
        System.out.println(name + " 汪汪叫!");
    }
}

// 使用
Dog myDog = new Dog("小黑");
myDog.bark(); // 输出:小黑 汪汪叫!

2. 抽象类(Abstract Class)

  • 定义:用 abstract 声明的类,不能直接实例化,可以包含抽象方法(无实现)和具体方法(有实现)。
  • 核心特点
    • 不可实例化:必须通过子类继承并实现抽象方法后才能使用。
    • 抽象方法:用 abstract 声明的方法,没有方法体(如 void run();)。
    • 混合实现:可以同时包含抽象方法和具体方法。
    • 构造方法:可以有构造方法(供子类初始化时调用)。
  • 适用场景:定义公共代码(如模板方法模式)和强制子类实现特定行为。

示例

// 抽象类:不能直接实例化
abstract class Animal {
    protected String name;

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

    // 抽象方法(子类必须实现)
    public abstract void makeSound();

    // 具体方法(子类可以直接继承)
    public void sleep() {
        System.out.println(name + " 正在睡觉...");
    }
}

// 子类必须实现抽象方法
class Cat extends Animal {
    public Cat(String name) {
        super(name);
    }

    @Override
    public void makeSound() {
        System.out.println(name + " 喵喵叫!");
    }
}

// 使用
Animal myCat = new Cat("小白");
myCat.makeSound(); // 输出:小白 喵喵叫!
myCat.sleep();     // 输出:小白 正在睡觉...

3. 接口(Interface)

  • 定义:用 interface 声明的类型,完全抽象(Java 8 前),定义一组方法规范,不包含具体实现。
  • 核心特点
    • 完全抽象(默认):所有方法默认是 public abstract 的(Java 8 前)。
    • 默认方法:Java 8 后允许用 default 定义具体方法。
    • 静态方法:Java 8 后允许定义静态方法。
    • 多继承:一个类可以实现多个接口。
    • 无状态:不能包含实例字段(只有 public static final 常量)。
  • 适用场景:定义行为规范(如 RunnableComparable)。

示例

// 接口:定义行为规范
interface Flyable {
    // 抽象方法(默认 public abstract)
    void fly();

    // 默认方法(Java 8+)
    default void land() {
        System.out.println("正在着陆...");
    }

    // 静态方法(Java 8+)
    static void checkFuel() {
        System.out.println("燃料检查完成!");
    }
}

// 实现接口
class Bird implements Flyable {
    @Override
    public void fly() {
        System.out.println("鸟儿在飞翔!");
    }
}

// 使用
Bird sparrow = new Bird();
sparrow.fly();   // 输出:鸟儿在飞翔!
sparrow.land();  // 输出:正在着陆...
Flyable.checkFuel(); // 输出:燃料检查完成!

三者的核心区别对比表

特性 普通类 抽象类 接口(Java 8+)
关键字 abstract interface
实例化 可以直接实例化 不能直接实例化 不能直接实例化
方法实现 所有方法必须实现 可以包含抽象方法和具体方法 默认只有抽象方法,但支持 defaultstatic 方法
字段 可以有实例字段 可以有实例字段 只能有 public static final 常量
构造方法
继承/实现 单继承(extends) 单继承(extends) 多实现(implements)
设计目的 具体实现 定义部分规范 + 公共代码 定义纯行为规范

关键使用场景总结

  1. 普通类

    • 需要直接实例化对象。
    • 所有方法都有具体实现。
    • 例如:工具类(StringUtils)、实体类(User)。
  2. 抽象类

    • 需要定义公共代码,同时强制子类实现某些方法。
    • 适合模板方法模式(定义算法骨架)。
    • 例如:InputStream(定义读取流程,子类实现具体细节)。
  3. 接口

    • 定义行为规范,不关心具体实现。
    • 需要实现多继承(如一个类同时实现 RunnableSerializable)。
    • 例如:Comparable(定义比较规则)、List(定义集合操作)。

如何选择?

  1. 优先用接口

    • 需要多继承。
    • 定义纯行为规范(如 Runnablerun())。
    • 例如:微服务中的 API 定义。
  2. 用抽象类

    • 需要复用代码(如公共方法)。
    • 需要部分方法由子类实现。
    • 例如:AbstractList 提供集合的通用实现,子类只需实现 get()size()
  3. 用普通类

    • 所有方法都已明确实现。
    • 不需要被扩展(用 final 修饰防止继承)。

常见误区

  1. 抽象类 vs 接口

    • 抽象类可以包含字段和构造方法,接口不能。
    • 接口可以实现多继承,抽象类只能单继承。
  2. Java 8 后的接口

    • 接口的 default 方法是为了向后兼容,避免破坏现有实现。
    • 如果多个接口有相同的 default 方法,实现类必须重写该方法。
  3. 抽象类可以有 main 方法

    • 抽象类可以有 main 方法,但不能实例化自身(只能通过子类)。

通过合理使用抽象类、普通类和接口,可以设计出高内聚、低耦合、易于扩展的代码结构。


网站公告

今日签到

点亮在社区的每一天
去签到