一、 抽象类与接口的区别
1. 设计理念不同
- 抽象类 (Abstract Class) :解决 "是什么 " 的问题。代表核心家族体系,提取同类事物的共性(如:猫和狗都是动物)。
- 接口 (Interface) :解决 "能做什么 " 的问题。代表一种能力契约或行为规范(如:鸟和飞机都能飞)。
| 维度 | 抽象类 (Abstract Class) | 接口 (Interface) |
|---|---|---|
| 设计理念 | (是什么,核心家族体系) | (能做什么,能力契约) |
| 成员变量 | 可以有普通变量(状态) | 只能是 public static final 常量 |
| 构造方法 | 有(用于子类初始化) | 无 |
| 方法实现 | 抽象方法 + 普通方法(代码复用) | 抽象方法 + default/static 方法 |
| 继承规则 | 单继承(只能继承一个) | 多实现(可实现多个) |
二、 抽象类与接口什么时候该用哪个呢
简单来说同宗同源,属于同一个核心家族体系,关联的是什么 就是选抽象类。
反之它们八竿子打不着,只是恰好具备了同一种能力,适合用接口来打补丁,且关联的是能做什么
【抽象类】:有状态(成员变量)、有构造器、单继承
java
abstract class Animal { String name; // 普通成员变量(状态)
public Animal(String name) { this.name = name; } // 构造方法
public void breathe() { System.out.println(name + "在呼吸"); } // 普通方法(代码复用)
public abstract void speak(); // 抽象方法(强制子类实现) }
【接口】:无状态(只有常量)、无构造器、多实现
java
interface Flyable { int MAX_SPEED = 300; // 只能是静态常量
(public static final) void fly(); // 抽象方法
// JDK8+ 支持 default 方法,但核心依然是定义行为契约
default void land() { System.out.println("平稳着陆"); } }
【实现】:单继承抽象类 + 多实现接口
java
class Bird extends Animal implements Flyable {
public Bird(String name) { super(name); }
@Override public void speak() { System.out.println(name + "叽叽喳喳"); }
@Override public void fly() { System.out.println(name + "展翅高飞"); } }
现代接口的演进(JDK 8+)
- 变化 :接口不再仅仅只有抽象方法,JDK 8 引入了
default默认方法和static静态方法。简单说default方法最核心的用途,就是在原有的接口中增加新方法 - 目的 :
default方法解决了接口升级的兼容性问题(老实现类不用强制重写新方法)。但接口依然没有构造器 ,也不能保存成员变量状态
三、 多态的"两面派"真相
在 Java 中,成员变量(Field)和成员方法(Method)的访问规则是完全不同的:
- 成员变量(字段):编译看左边,运行 也看左边。
- 成员方法(非静态):编译看左边,运行看右边。
java
class Parent {
int x = 10;//成员变量
}
class Child extends Parent {
int x = 20;
}
public class Test {
public static void main(String[] args) {
Parent p = new Child();
System.out.println(p.x);
}
}
// 输出结果是 10(运行看左边)
java
class Parent {
void show() { System.out.println("Parent show"); }
}
class Child extends Parent { @Override
void show() { System.out.println("Child show"); }
}
Parent p = new Child();
p.show();
// 输出 "Child show"(这就是"运行看右边")
多态的底层原理
- 变量是静态绑定:编译器在编译时就根据引用类型(左边)确定了内存偏移量,所以运行期不会变。
- 方法是动态绑定 :JVM 在运行时会通过对象的虚方法表(vtable) 去查找实际子类重写后的方法地址。
多态的弊端与实战避坑
- 弊端:多态的父类引用无法直接调用子类特有的方法。
- 解决 :必须使用向下转型(强转) 。
- 避坑指南 :强转前必须用
instanceof关键字判断,否则会报ClassCastException崩溃。
四、 多态生效的三大前提(面试必问)
在谈多态时,必须满足以下三个硬性条件,缺一不可:
- 有继承/实现关系(基础)
- 有方法重写(核心)
- 有父类引用指向子类对象(触发条件)