🔷 第九章 面向对象编程基础
📄 一句话概括

第九章讲的是"换一副眼镜看程序"。三块内容:为什么要换(9.1,程序长大后会卡在"数据敞开、改动扩散")、换成了什么样(9.2,以对象为中心组织程序 + 封装/继承/多态三大特点)、怎么把想法画出来(9.3,用 UML 类图表达设计)。这一章一行新语法都没学——它做的是把脑子转过来:先明白"为什么要有对象",下一章的 class 才不会学成一堆背不住的规则。

📝 本节和前面几节的关系

这一章是全书的分水岭:第一章到第八章,我们一直在"面向过程"的地基上盖楼(类型 → 表达式 → 分支循环 → 函数 → 数组指针 → 结构体);这一章换了个地基——同样的砖(变量、函数、指针),改用"对象"的方式砌。结构体是"类的雏形"(8.1、8.7 都提过),本章把这个雏形为什么不够、缺什么说透了:缺"把方法也包进去"和"把数据锁起来"。 下一章就是把这缺的两块补上。

一、本章知识地图

块 小节 一句话要点
为什么要换角度 9.1 面向对象不是新语法,是新角度;程序长大后会卡在"数据敞开 → 改动扩散";对象 = 属性 + 方法,数据要藏起来
两种组织方式 9.2 / 9.2.1 / 9.2.2 面向过程按"步骤"组织(数据与函数两分离),面向对象按"角色"组织(数据与方法绑一起);不是新打败旧,是"小"和"大"之分
三大特点 9.2.3 封装(藏数据、只留门,治"乱改")/继承(子承父业、只写多的,治"重复")/多态(同一句话不同反应,治"一堆 if-else")——三个独立的能力,可以只用其中一个
UML 是什么 9.3 / 9.3.1 画设计的通用图形语言;Booch + OMT + OOSE 三套画法合流,1997 年成为 OMG 标准;它是记号标准,不是编程语言、不是开发方法
UML 的结构 9.3.2 结构图(有哪些东西)vs 行为图(怎么动);类图三格框(类名/属性/方法)+ 可见性 + - # ~ + 五种关系(依赖→关联→聚合→组合→泛化)+ 多重性
怎么建模 9.3.3 四步法:找对象(名词)→ 定属性 → 定行为(改谁的数据就写谁身上)→ 定关系(is-a / has-a / 整存部分否);画完自问三句;图要跟着代码改

二、必须记住的七个结论

  1. 面向对象是一种"看程序的角度",不是一种语法。同一个程序两种思路都能写;class 只是让"面向对象思路"写起来最顺手的语法支撑(9.1)。
  2. 对象 = 数据(属性)+ 操作数据的方法(行为),而且数据通常要藏起来。判断"是不是面向对象"的关键,不是有没有写 class,而是"数据和方法有没有绑在一起、数据有没有被保护"(9.2.2)。
  3. 类和对象是"图纸"和"实物"。类只描述"有什么属性和方法"(一般只写一份);对象是按图纸造出来的实体,各自有独立的状态,想造多少造多少(9.2.2)。
  4. 面向过程不是"过时的写法"。流程清晰、变化少、规模小或要拼性能的场景,它就是更合适的选择;两者常常混着用——对象负责宏观分工,对象内部的方法依然是步骤(9.2.1、9.2)。
  5. 三大特点各治一种病:封装治"乱改"、继承治"重复"、多态治"越写越长的 if-else"。三个可以独立使用——只用封装也完全合法(9.2.3)。
  6. 判断该不该继承,只看一条:是不是"是一种"(is-a)。"有一个"(has-a)要用成员变量包含。把 has-a 画成继承,是新手最典型的设计错误(9.2.3、9.3.3)。
  7. UML 图的读法:先看方框分几格(类名/属性/方法),再看每条线的头(符号 = 关系种类:空心三角=继承、实心菱形=组合、空心菱形=聚合、普通实线=关联、虚线箭头=依赖)和尾(数字 = 多重性),最后看线指向谁(9.3.2)。

三、动手 Demo:两关自测

第一关(画图):把"校园快递柜"建模

需求(一句话):

"快递员存包裹到快递柜,学生凭取件码从柜子里取走包裹。"

按四步建模法做出来,产出一张类图(纸笔或文本方框均可):

第①步 圈名词:快递员、包裹、快递柜、学生、取件码

第②③步 列属性与行为(提示:"这件事改谁的数据,就写谁身上")

类 属性 方法
Student 姓名、手机号 + takeOut(code: string) : bool
Package 单号、取件码、是否已取 + pick() : bool
Locker 全部格口、空位数 + store(p: Package) : bool、+ find(code) : Package

第④步 定关系(对着每两个类问那三句快问)

一对 结论
Locker ↔ Package 组合 ◆(柜子清空了,里面的包裹也就不在这个系统里了)—— 标 1 对 *
Student ↔ Package 关联(取走 ≠ 拥有系统记录;包裹不属于学生)—— 标 1 对 *

自问三句: 1. 职责清不清?—— Student 不该负责"判断取件码对不对",那是 Locker 的活(它才知道自己的格口状态)。 2. 关系对不对?—— 检查菱形画在整体(Locker)那端。 3. 有多余的类吗?—— "取件码"不是一个类,它是 Package 的一个属性(一个名词不等于一个类)。

看到什么算成功:你能画出三格方框、标出可见性符号、连出一条实心菱形线和一条普通实线,并能说清"为什么取件码不是类"。

第二关(判断对错):八种说法

下面八句话,判断对错并说明理由:

# 说法 判断与理由
① 用了 class 就是面向对象编程 错。若成员全公开、方法都在操作外部数据,只是"穿着对象外衣的面向过程"(9.2)
② 面向对象比面向过程先进,所以应该一律用它 错。选择看规模与变化;小工具、算法、底层用面向过程更合适(9.2.1)
③ struct Student 定义了,就分配了内存 错。定义只是画图纸;Student s1; 才分配(8.1)
④ 学生"有一个"书包,所以 Student 应该继承 Bag 错。has-a 要用成员变量包含;继承只用于 is-a(9.2.3)
⑤ 多态就是函数重载 错。重载是编译期按参数表定;多态是运行期按对象实际类型定(9.2.3)
⑥ 封装就是给每个属性配一对 get/set 欠妥。这等于"门开得跟没墙一样";好的封装是只暴露有意义的行为(9.2.3)
⑦ UML 是一种能生成代码的编程语言 错。它是图形记号标准,不写可执行代码、不能编译(9.3.1)
⑧ 类图里,聚合和组合可以随便挑一个画 错。判据是"整体消亡时,部分能不能独立存在":能 → 聚合◇;不能 → 组合◆(9.3.2)

30 秒实验:把第 ④ 句的答案写进代码里体会一下——用结构体嵌套写"学生有一个书包"(struct Student { Bag bag; ... };),你会发现这比"继承"自然得多。"is-a 用继承、has-a 用包含"这条规矩,不是背出来的,是写出来的。

四、常见误区(本章汇总)

  1. "面向对象就是多写类"——核心是"数据与方法绑在一起、数据被保护"。形式像、实质不像的"伪对象",是本章最该警惕的东西。
  2. "三大特点必须一起用"——独立工具,各治一病。只用封装非常常见;不继承、不多态的类到处都是。
  3. "先写类,设计自然就有了"——顺序反了。先想角色与职责,再落成类;直接开写,最后往往得到一堆"什么都干"的怪物类(9.3.3)。
  4. "图是画给人看的,画完就完事"——图是活的。代码变了图不改,图就从资产变成误导(9.3.3)。
  5. "这一章不重要,可以跳过"——最不该跳。跳过概念直接学 class,会"会写但不知道为什么",最后只能靠背规则,一遇到设计题就卡住。这一章是"设计意识"的起点。
  6. "面向对象能跑得更快"——它带来的是"好维护",不是"跑得快"。要拼极致性能的地方,反而是面向过程/底层写法的天下(9.2)。
自测八问(点开对答案) 1. 面向对象最核心的一句判断标准是什么?——**"数据和方法有没有绑在一起、数据有没有被保护"**——而不是"有没有写 `class`"。 2. 类和对象分别是什么?——类是**图纸/模板**(描述有什么属性和方法);对象是**按图纸造出来的实物**(有自己的状态、占内存、可造多个)。 3. 封装解决什么问题?本质是什么?——治"**数据被乱改、规则被绕过**";本质是**"约定入口"**——数据藏起来是手段,**让所有修改都必须走规定的门**才是目的。 4. 该不该用继承,判断标准是什么?——**是不是"是一种"(is-a)关系**。"有一个"(has-a)要用成员变量包含。 5. 多态和重载的区别?——**重载**:编译期按参数表决定调用哪个同名函数;**多态**:运行期按对象实际类型决定执行哪份实现(靠虚函数)。 6. UML 是什么?它是编程语言或开发方法吗?——**统一建模语言**,一套**画设计图的符号标准**;**都不是**——它不写可执行代码、不能编译,也不规定项目流程。 7. 类图的方框分哪三格?`+` `-` `#` 各表示什么?——**类名/属性/方法**;`+` 公开、`-` 私有、`#` 保护(子类可见)、`~` 包内可见。 8. 面向对象建模的四步是什么?画完要自问哪三句?——**找对象 → 定属性 → 定行为 → 定关系**;自问**职责清不清 / 关系对不对 / 有没有多余的类**。

相关笔记