面向对象(OOP)是 Java 的根基,封装、继承、多态这三个词天天说,但"多态到底怎么实现的、接口和抽象类到底怎么选"一直没系统梳理。这篇把 OOP 最核心的几块讲清楚,尤其是面试高频的"多态"和"接口 vs 抽象类"。
一、为什么要有面向对象
面向过程(C 语言)是"按步骤写函数",数据和方法分离。项目一大,数据和操作数据的方法散落各处,难维护。面向对象把"数据 + 操作数据的方法"绑成一个对象,用三大特性组织代码:
- 封装:隐藏内部细节,只暴露接口
- 继承:复用已有代码
- 多态:一个接口,多种实现
二、封装:隐藏内部,只暴露接口
封装 = private 字段 + 公开方法,外界不能直接碰内部数据:
java
public class BankAccount {
private double balance; // 私有:外部不能直接改
public void deposit(double amount) { // 公开方法:唯一的入口
if (amount <= 0) throw new IllegalArgumentException("金额必须 > 0");
balance += amount; // 业务校验封装在方法里
}
public double getBalance() { return balance; }
}好处:balance 只能通过 deposit 改,校验逻辑集中在一处,外部改不了脏数据。
三、继承:复用代码
子类 extends 父类,自动拥有父类的字段和方法:
java
public class Animal {
protected String name;
public void eat() { System.out.println(name + " 吃东西"); }
}
public class Dog extends Animal { // Dog 复用 Animal 的 name、eat()
public void bark() { System.out.println(name + " 汪汪"); }
}继承解决"代码重复",但注意:Java 是单继承(一个类只能有一个父类),想多"继承"能力要靠接口。
四、多态:一个引用,多种行为(核心)
多态 = 父类引用指向子类对象,运行时调用子类的实现:
java
Animal animal = new Dog(); // 编译类型是 Animal,运行类型是 Dog
animal.eat(); // 运行时调用 Dog 的 eat()(如果 Dog 重写了)
// 同一个方法,传不同子类,表现不同
void feed(Animal a) { a.eat(); } // 传 Dog、Cat 都行,各吃各的多态的实现机制是"动态绑定":编译时 animal.eat() 不知道调谁,运行时根据对象实际类型(Dog)决定调 Dog 的 eat()。
多态的三个条件:继承 / 重写 / 父类引用指向子类,缺一不可。
五、重载 vs 重写(面试高频,别混)
| 维度 | 重载(Overload) | 重写(Override) |
|---|---|---|
| 是什么 | 同方法名,不同参数列表 | 子类覆盖父类方法 |
| 发生时机 | 编译期决定(静态多态) | 运行期决定(动态多态) |
| 参数 | 必须不同 | 必须相同 |
| 返回值 | 可不同 | 必须相同(或协变) |
java
// 重载:同名方法,参数不同
public void print(int x) { ... }
public void print(String s) { ... }
// 重写:子类覆盖父类
class Animal { void sound() { System.out.println("叫"); } }
class Dog extends Animal { @Override void sound() { System.out.println("汪汪"); } }六、接口 vs 抽象类(面试必问)
两者都是"抽象",但语义不同:
| 维度 | 接口(interface) | 抽象类(abstract class) |
|---|---|---|
| 语义 | "能做什么"(能力/协议) | "是什么"(共性基类) |
| 继承 | 可实现多个接口 | 只能继承一个(单继承) |
| 成员变量 | 只能 public static final 常量 | 可以有普通成员变量 |
| 构造器 | 没有 | 有(子类 super 调用) |
| 方法 | 抽象方法(Java 8 起可有 default) | 抽象方法 + 具体方法 |
一句话选型:表达"能力"(会飞、能序列化)用接口;表达"共性"(动物都有名字和吃的方法)用抽象类。需要多继承能力时,接口是唯一选择。
java
// 接口:表达能力
interface Flyable { void fly(); }
interface Swimmable { void swim(); }
class Duck implements Flyable, Swimmable { ... } // 实现多个接口
// 抽象类:表达共性
abstract class Animal {
protected String name; // 普通成员变量
abstract void sound(); // 抽象方法
void eat() { System.out.println("吃东西"); } // 具体方法
}小结
- 封装:
private字段 + 公开方法,隐藏内部 - 继承:
extends复用代码,Java 单继承 - 多态:父类引用指向子类,运行时动态绑定(继承 + 重写 + 父类引用)
- 重载 = 编译期(同方法名不同参数);重写 = 运行期(子类覆盖父类)
- 接口表达"能力"(多实现),抽象类表达"共性"(单继承)
OOP 是写 Java 的基本功。想了解 OOP 之上怎么用这些特性组织代码,看「架构设计 → 设计模式」——设计模式就是 OOP 三大特性的组合套路。
