面向对象思想概述
本教程共 100 篇 · 第 29 篇 · 更新于 2026-08-05 · 约 6 分钟阅读
29. 面向对象思想概述
本节目标:理解面向对象为什么出现、它和面向过程的差别,并记住封装、继承、多态这三大特性的核心含义。
前面写的代码都是”按步骤一步步走”,这种思路叫面向过程。程序小还好,一大就乱。面向对象(OOP)换了个角度看世界:把数据和操作数据的行为打包成一个个”对象”。这一章不写具体语法,先把地图看明白,后面几章再逐个落地。
从”过程”到”对象”
面向过程像写菜谱:第一步切菜、第二步下锅、第三步装盘,强调动作顺序。面向对象像开餐厅:有”厨师”对象、“菜品”对象、“订单”对象,每个对象管自己的一摊事,互相配合完成目标。
// 面向过程:关心怎么做
void cook(String name) { ... }
// 面向对象:关心谁来做
Chef chef = new Chef();
chef.cook("红烧肉");
```bash
后者把"会做菜"的能力和数据(厨师姓名、等级)绑在 Chef 对象上,用起来更自然。当系统变复杂,"谁来做"比"怎么做"更不容易乱。
> [!NOTE]
> 面向过程不等于落后。小程序、脚本、算法题里用面向过程更顺手。真正的大型工程才需要面向对象的组织能力。
## 封装:把细节藏起来
封装是把对象的内部数据和保护它的方法捆在一起,只留必要的接口给外面用。就像开车,你踩油门就能走,不用懂发动机怎么点火。
好处:外面只能按你规定的方式用,内部怎么实现随你改,不影响别人。后面学 private 和 getter/setter 就是封装的具体手段。封装降低耦合,是大型项目能协作的前提。
### 封装解决什么问题
假设你写了个类,里面有温度字段。如果直接暴露,外面可以随便设成绝对零度以下,物理上就不合法了。封装让你在 setter 里加校验:
```java
public class Temperature {
private double celsius; // 私有,外面不能直接改
public void setCelsius(double c) {
if (c < -273.15) {
throw new IllegalArgumentException("低于绝对零度");
}
this.celsius = c;
}
}
外面调 setCelsius(-300) 直接被拒绝,规则被保护住。这就是封装的价值:在”入口”挡掉非法数据。
继承:复用已有的能力
继承让一个新类直接拥有旧类的属性和方法,不用重写一遍。比如”学生”和”老师”都是”人”,都有姓名年龄,那就先写个 Person,再让 Student、Teacher 继承它。
class Person { String name; }
class Student extends Person { int grade; } // Student 自动有了 name
```bash
继承表达的是"是一个"的关系:Student 是一个 Person。它把共性往上提,差异往下放,避免重复代码。
> [!NOTE]
> Java 只支持**单继承**:一个类只能有一个父类。想复用多份能力,用后面讲的接口(interface)。单继承让类之间的关系更清晰,不会陷入"多重继承"的歧义泥潭。
### 继承的两个陷阱
一是"为了继承而继承"。如果两个类只是碰巧有相同字段,没有"是一个"的关系,硬继承只会让类层次越来越别扭。比如 Car 和 Engine,不应该是继承关系,应该是组合关系(Car 有一个 Engine)。
二是"层次过深"。A 继承 B,B 继承 C,C 继承 D……五六层之后,改一处底层代码心里都没底。一般建议继承层次不超过三层。
## 多态:同一动作,不同表现
多态指"同一方法,不同对象表现出不同行为"。比如 Animal 有个 shout 方法,Cat 叫出"喵",Dog 叫出"汪"。调用时只管 `animal.shout()`,具体叫什么由运行时真正对象的类型决定。
这让代码能"面向父类写,灵活接子类",是面向对象最灵活也最难懂的一块。后面会专门用一章展开。多态让程序"对扩展开放、对修改关闭",是框架设计的基石。
### 多态为什么强大
假设有个系统要处理各种形状(圆、方、三角)的面积。如果没有多态,得写一堆 if-else 判断类型。有了多态,只需:
```java
Shape s = getSomeShape(); // 可能是圆,可能是方
s.area(); // 自动调正确版本
新增一种形状,加一个子类就行,旧代码零改动。这就是”对扩展开放、对修改关闭”。
三大特性串起来看
封装解决”怎么藏”,继承解决”怎么复用”,多态解决”怎么灵活”。三者配合,才能写出既稳又能长个子的代码。初学时不用强求一次全懂,先建立印象即可。
| 特性 | 核心问题 | 手段 | 关键词 |
|---|---|---|---|
| 封装 | 怎么保护数据 | private、getter/setter | 隐藏细节 |
| 继承 | 怎么复用代码 | extends | 是一个 |
| 多态 | 怎么灵活替换 | 父类引用指向子类对象 | 同一接口不同实现 |
为什么学面向对象
- 代码更好组织:相关的数据和行为待在一起,找起来快。
- 更易复用:继承、组合避免重复造轮子。
- 更易维护:改一处实现,接口不变就不影响其他代码。
- 更抗变化:多态让新增类型不改动旧逻辑。
Tip初学不必强求”一开始就面向对象设计”。先把类和对象的基本写法练熟,三大特性会在后面逐章落地。这章先把地图看明白,别急着背定义。
面向对象不是银弹
也要说句公道话:不是所有问题都该面向对象。简单脚本、纯计算,面向过程反而更直接。Java 强制你用类组织代码,但并不意味着每行都要追求”完美对象”。理解思想,比套用术语重要。
速查表
| 概念 | 一句话 |
|---|---|
| 类(Class) | 对象的”图纸” |
| 对象(Object) | 类的”实例” |
| 封装 | 数据私有化,方法公开化 |
| 继承 | is-a,子类复用父类 |
| 多态 | 同一调用,不同行为 |
| 抽象 | 只定义接口,不写实现 |
| 组合 | has-a,优先于继承 |
常见疑问
Q:面向对象和面向过程到底哪个更好? 没有更好,只有更适合。算法题、脚本工具适合面向过程;中大型软件、团队协作适合面向对象。Java 是面向对象语言,所以这本教程后面的代码都以 OOP 为主。
Q:封装就是 private + getter/setter 吗? 这是最常见的封装手段,但不等于全部。只读字段可以不写 setter;复杂校验逻辑放 setter 里;方法也可以封装——private 方法对外不可见,但内部调用。
Q:为什么现在推荐”组合优于继承”? 继承层次一深,改父类影响所有子类,容易失控。组合是把对象当作字段注入,灵活且低耦合。后面学到接口和依赖注入时,会深讲这一点。