第18篇:Java接口和抽象类的异同,default关键字

📌 系列导航《Java 100 天进阶之路》完整目录 |
⬅️ 上一篇:第17篇:Java常用包装类与自动装箱拆箱深入|
➡️ 下一篇:第19篇:Java接口的作用和意义

一、核心知识点

  • 接口(interface)和抽象类(abstract class)的定义与语法
  • 相同点:都不能实例化,都可以包含抽象方法
  • 不同点:
    • 抽象类可以有状态(成员变量)、构造方法、具体方法;接口早期只能有常量(public static final)和抽象方法(Java 8 后可以有 default/static 方法,Java 9 可以有 private 方法)
    • 一个类只能继承一个抽象类(单继承),但可以实现多个接口
    • 抽象类成员的访问修饰符任意;接口方法默认 public,变量默认 public static final
    • 抽象类是 is-a 关系,接口是 can-do 关系(能力)
  • default 关键字:Java 8 引入,接口中可以提供默认实现,子类可重写也可不重写,用于接口演进
  • 设计选择原则:优先使用接口(多实现),需要共享状态或通用行为时使用抽象类

二、通俗讲解(1分钟开心学)

1. 抽象类——不完整的类

抽象类就像一张“半成品图纸”,它定义了一些通用属性和方法,但某些关键步骤留空,等着子类去填充。

  • 可以有构造方法(被子类调用)
  • 可以有自己的成员变量(不是常量)
  • 可以有普通方法(有方法体)
  • abstract 修饰的方法没有方法体,必须由子类实现

2. 接口——纯粹的能力规范

接口是一份“合同”或“能力证书”,它只告诉你“你能做什么”,不告诉你“怎么做”。

  • 完全没有状态(不能有实例变量,只能有静态常量)
  • 方法默认是 public abstract(Java 8 之前)
  • 一个类可以实现多个接口(多继承的替代方案)

生活类比
抽象类就像“汽车设计图纸”:它画好了底盘、方向盘、刹车的位置,但引擎具体用哪种、轮胎尺寸多少,留给你决定。
接口就像“可飞行资质”:只要你能飞,不管你是鸟、飞机还是超人,都算。你不需要继承“飞行器”类,只需要实现“飞行”接口。

3. default 方法——接口的“救生圈”

Java 8 之前,接口一旦发布,就不能再添加新方法,否则所有实现类都要被迫修改。default 方法允许给接口添加新方法并提供默认实现,实现类可以不改,也可以重写。

4. 如何选择?

  • 当你需要定义 “是什么” (is-a)且需要共享代码时,用抽象类
  • 当你需要定义 “能做什么” (can-do)且可能有多种不相关的实现时,用接口
  • 实际工程中常常 “接口 + 抽象基类” 搭配使用(如 List 接口 + AbstractList 抽象类)

三、实操代码案例 + 场景说明

场景:设计一个游戏中的角色系统。所有角色都有生命值(共享状态),并且部分角色可以飞行(能力)。

1. 抽象类示例(共享状态 + 通用方法)

abstract class GameCharacter {
    protected String name;
    protected int hp;
    
    public GameCharacter(String name, int hp) {
        this.name = name;
        this.hp = hp;
    }
    
    // 抽象方法:不同角色攻击方式不同
    public abstract void attack();
    
    // 普通方法:所有角色通用
    public void takeDamage(int damage) {
        hp -= damage;
        System.out.println(name + " 受到 " + damage + " 伤害,剩余生命 " + hp);
    }
}

class Warrior extends GameCharacter {
    public Warrior(String name) { super(name, 100); }
    @Override
    public void attack() {
        System.out.println(name + " 用剑攻击!");
    }
}

2. 接口示例(能力)+ default 方法

interface Flyable {
    void fly();  // 抽象方法
    
    // default 方法:提供默认实现,实现类可以不重写
    default void takeOff() {
        System.out.println("准备起飞...");
    }
}

class Bird extends GameCharacter implements Flyable {
    public Bird(String name) { super(name, 50); }
    @Override
    public void attack() { System.out.println(name + " 啄击!"); }
    @Override
    public void fly() { System.out.println(name + " 拍翅膀飞翔"); }
    // takeOff() 可以直接使用 default 实现
}

class Plane implements Flyable {
    @Override
    public void fly() { System.out.println("引擎推动飞行"); }
    // 也可以重写 takeOff
    @Override
    public void takeOff() {
        System.out.println("滑行加速,拉起机头");
    }
}

3. 接口与抽象类的异同比对表

特性 抽象类 接口(Java 8+)
实例化 不能 不能
成员变量 可以是任意类型 只能是 public static final
构造方法 可以有 不能有
继承/实现 单继承 多实现
访问修饰符 任意 方法默认 public(Java 9 允许 private 方法)
非抽象方法 可以有普通方法 通过 default / static / private 提供

四、避坑要点

错误/误区 后果 正确做法
抽象类的构造方法写成 public 虽能编译,但只能被子类调用,单独 new 会报错 通常用 protected,更语义化
接口中定义成员变量(非常量) 编译错误 需要共享数据,用抽象类
两个接口有同名的 default 方法 实现类必须重写解决冲突 在实现类中重写,并可用 InterfaceName.super.method() 指定
认为 default 方法可以被静态调用 编译错误 default 方法只能通过实例调用

五、面试高频考点

Q1:抽象类和接口的区别(至少5点)?

  1. 抽象类有构造方法,接口没有。2. 抽象类可以有非 final 成员变量,接口只能是常量。3. 抽象类方法可以用各种修饰符,接口方法默认 public。4. 类单继承抽象类,多实现接口。5. 抽象类用于 is-a,接口用于 can-do

Q2:为什么 Java 8 要引入 default 方法?

为了在保持向后兼容的前提下给现有接口增加新方法(如 Collection.stream()),避免所有实现类被迫修改。

Q3:一个类实现两个接口都有同名的 default 方法,怎么办?

必须在该类中重写该方法。可以调用其中一个父接口的默认方法:Interface1.super.method()

六、练习题

  1. 设计:定义抽象类 Vehicle(属性:品牌、速度,抽象方法:run()),接口 Electric(方法:charge())。创建 ElectricCar 类继承 Vehicle 并实现 Electric
  2. 代码分析:为什么接口中的变量默认是 public static final?如果试图修改会怎样?
  3. 动手:写两个接口 AB,都有 default void test(),然后写一个类实现两者,重写 test() 并分别调用两个父接口的默认方法。

📊 你的学习进度

  • 当前:第18篇 / 共44篇 · 第二阶段:核心语法与面向对象(第5~20篇)
  • ✅ 已完成:第1~17篇
  • 📖 正在学:第18篇
  • ⏳ 待学习:第19~44篇

👉 📚 完整目录 & 学习指南 | 🔥 订阅本专栏,不错过每一篇

💡 本专栏每篇都包含:避坑表 + 面试高频考点 + 练习题。每天30分钟,100天拿offer!


👉 下一篇预告

《Java接口的作用和意义》

内容简介:接口的解耦、多态、扩展性、标准化作用,面向接口编程,在框架中的应用。

💡 学完这篇,你将真正理解“面向接口编程”的含义,写出更灵活的代码。

📌 《Java 100 天进阶之路 | 从入门到上岗就业》 每天一篇,建议收藏 + 关注,一起100天拿offer!
👉 点击关注我,更新后第一时间收到推送!

更多推荐