💻 综合学习笔记:面向对象与进阶编程 (9645)

你好,未来的计算机科学家!欢迎来到面向对象编程 (OOP) 及其他关键进阶编程技术章节。这些概念起初可能显得有些抽象,但它们是现代大规模软件开发的基础。别担心!我们会将它们拆解成简单易懂的小模块。读完这一章,你将掌握如何设计稳健、灵活且可重用的代码。

让我们开始吧!


1. 面向对象编程 (OOP) 基础 (3.9.1)

OOP 是一种强大的编程范式,它围绕对象 (Objects) 而非仅仅是函数和逻辑来构建代码。它通过在程序中直接对现实世界中的实体进行建模,帮助我们管理复杂性。

什么是类 (Class)?(蓝图)

类 (Class) 本质上是创建对象的蓝图、模板或定义。它规定了该类型所有对象将拥有的通用特征(数据)和行为(动作)。

  • 类比: 把类想象成汽车工厂的蓝图。它规定了每一辆车都必须有四个轮子、一个引擎,以及诸如 start()(启动)和 accelerate()(加速)之类的方法。
属性 / 特征 (Properties / Attributes)

这些是定义对象状态的数据字段,用于记录对象的特征。

  • 示例: 如果类是 Car(汽车),那么属性就是 colour(颜色)、speed(速度)和 fuelLevel(油量)。
方法 (Methods)

这些是在类中定义的函数或过程,决定了对象能够执行的动作或行为。

  • 示例: 对于 Car 类,方法包括 startEngine()(启动引擎)、brake()(刹车)或 refuel()(加油)。

什么是对象 (Object)?(实体)

对象 (Object) 是类的一个具体实例。一旦你创建了一个对象,它就拥有了属性的具体且明确的值。

  • 类比: 汽车蓝图(类)是抽象的。而停在外面那辆红色的丰田卡罗拉(对象)是具体的,它是根据那份蓝图实例化出来的。
实例化 (Instantiation)

实例化是将类转化为对象的过程。当你实例化一个类时,你是在要求计算机为该特定对象及其唯一的数据分配内存空间。

构造函数 (Constructors)

构造函数是一种特殊的方法,在对象被实例化时自动调用。它的主要任务是将对象初始化为预设状态

  • 如果你创建了一个 Car 对象,构造函数可能会自动将初始 speed 设为 0,将 fuelLevel 设为 50 升。
快速回顾:OOP 核心

类 (Class): 蓝图(定义结构)
对象 (Object): 实例(实际构建出来的东西)
构造函数 (Constructor): 初始化器(设置对象的初始数据)


2. 封装 (Encapsulation)(隐藏细节)(3.9.2)

封装是 OOP 的支柱之一。它意味着将数据(属性/特征)和操作这些数据的方法捆绑在一个单元(类)中,更关键的是:向其他类隐藏类内部的运作方式以及数据的具体呈现方式

为什么要使用封装?

它能保护数据的完整性。如果内部数据只能通过受控的方法(而非直接)进行修改,你就能确保数据始终有效。(例如:你可以确保速度永远不会低于零。)

访问修饰符 (Access Modifiers / Access Specifiers)

访问修饰符是用于定义类成员(属性和方法)可见性和可访问性的关键字。

  • 公共 (Public, +): 在程序中的任何地方均可访问,包括类外部。(类比:汽车喇叭——任何人都能按。
  • 私有 (Private, -): 仅能在声明它们的类内部访问。(类比:内部引擎温度表——只有引擎相关的方法需要读取它。
  • 受保护 (Protected, #): 在类本身以及任何继承该类的子类中均可访问。(类比:只有汽车及其特种卡车版本才知道的维护程序。
Getter 和 Setter 方法

由于私有数据无法被类外部的对象直接访问,我们使用称为 GetterSetter 的特殊公共方法来提供受控访问。

  • Getter: 用于检索(获取)私有属性值的方法。
  • Setter: 用于修改(设置)私有属性值的方法。Setter 通常包含验证逻辑,以确保新值合理。

类比: 想象一个银行账户。账户余额(数据)是私有的。你不能直接进去改数字,必须通过 deposit()(存钱,Setter)或 checkBalance()(查余额,Getter)这类公共方法。这些方法保护了数据。


3. 类之间的关系 (3.9.3)

现实世界的系统涉及许多交互组件。在 OOP 中,类必须形成特定的关系来模拟这种交互。

继承 (Inheritance)(“是一种”关系)

继承是一种关系,其中一个类(子类派生类)是现有类(基类父类)的更专门化版本。

  • 它促进了代码重用,因为子类会自动获得基类的所有属性和方法。
  • 示例: Truck(卡车)类(子类)继承自 Vehicle(车辆)类(基类)。卡车“是一种”车辆。
重写 (Overriding)

重写是指子类重新定义了从基类继承来的方法。这允许子类在保持方法名相同的情况下,表现出特定的行为。

  • 示例: CarMotorcycle(都是 Vehicle 的子类)都有 drive() 方法。但 Motorcycledrive() 方法会被重写,以包含针对两轮而非四轮的特定行为。

关联 (Association)(“使用”关系)

关联是两个对象之间的一种通用关系,其中一个对象可以使用另一个对象。

  • 这是一种比继承更弱的关系。
  • 示例: 在足球模拟中,Team(球队)对象与多个 Player(球员)对象相关联。球队“使用”球员。
类图符号 (Class Diagrams Notation)

你必须熟悉基本的 UML(统一建模语言)表示法来展示这些关系:

  • 继承: 用实线连接,并带有空心箭头,从子类指向基类
  • 关联: 用类之间的实线表示,通常带有标签来描述关系。
  • 图中的访问修饰符:
    • + (Public)
    • - (Private)
    • # (Protected)

4. 进阶编程技术 (3.9.4)

4.1 文件处理(文本文件)(3.9.4.1)

在“进阶编程”部分,你需要了解如何通过读取和写入文本文件来与计算机的持久存储进行交互。

  • 写入: 打开文件,逐行发送数据(字符串),并关闭文件以永久保存更改。
  • 读取: 打开文件,依次从文件中检索数据(字符串),并关闭文件。

文本文件至关重要,因为它们允许程序存储配置设置、用户输入或需要在程序停止运行后仍然存在的结果。

4.2 递归(调用自身的子程序)(3.9.4.2)

递归子程序是调用自身的函数或过程。这是一个强大的工具,通常用于可以将问题拆解为相同且规模更小的子问题的情况,例如计算阶乘或遍历树结构。

为了防止递归子程序无限运行(死循环,导致栈溢出错误),每个递归函数必须包含两个关键要素:

1. 基准情形 (Base Case)

基准情形是停止条件。这是子程序不再调用自身的情况。当达到此条件时,函数返回结果,递归过程开始回溯。

  • 如果你忘记了基准情形,程序就会崩溃!
2. 递归情形 (Recursive Case)

递归情形是子程序调用自身的部分,通常使用更小的输入,使其不断趋近于基准情形。

逐步示例:计算阶乘 (n!)

阶乘定义为 \(n! = n \times (n-1) \times (n-2) \times \dots \times 1\)。

让我们定义一个递归函数 Factorial(n)

  1. 基准情形: 如果 \(n = 0\),返回 1。(定义 0! 为 1。)
  2. 递归情形: 如果 \(n > 0\),返回 \(n \times \text{Factorial}(n-1)\)。

追踪 Factorial(4):

Factorial(4) 调用 4 * Factorial(3)
Factorial(3) 调用 3 * Factorial(2)
↳ ↳ Factorial(2) 调用 2 * Factorial(1)
↳ ↳ ↳ Factorial(1) 调用 1 * Factorial(0)
↳ ↳ ↳ ↳ Factorial(0) 触发 基准情形,返回 1。
↳ ↳ ↳ 返回 1 * 1 = 1
↳ ↳ 返回 2 * 1 = 2
↳ 返回 3 * 2 = 6
↳ 返回 4 * 6 = 24

最终结果为 24。

💡 递归记忆法:S-U-R

追踪递归时,记住这个流程:
1. S (Start) 开始: 调用函数。
2. U (Unwind) 展开: 继续调用(压栈),直到达到基准情形。
3. R (Return) 返回: 一旦触及基准情形,将结果逐层返回到栈中,得到最终答案。


进阶编程学习要点

OOP 的概念——类、对象、继承和封装——能帮你编写模块化、易于调试且可重用的代码。理解文件处理能确保你的数据持久化,而掌握递归则为你提供了一种高效处理复杂、自相似问题的优雅方式。

多加练习追踪(tracing)技巧,尤其是递归和 OOP 的基础概念(如实例化),你就能完全掌握这一章节!