Menu
CoddyTech

封装是什么?

在面向对象编程中,封装(encapsulation)是指把对象的数据和操作这些数据的方法放在同一个单元(通常是一个类)里,并隐藏内部细节,让其他代码只能通过对象的公有方法来使用它。

作者 Kevin Spektor, 联合创始人兼 CTO

更新于 2026年9月24日

假设一个银行应用把每个账户的余额存放在一个普通变量里。程序中任何地方的任何一行代码都可以写 account.balance = -500,没有什么能阻止它。当生产环境中出现负余额时,你只能在整个代码库里找出每一个碰过它的地方。封装解决了这个问题:余额只能通过少数几个方法来修改,而这些方法会强制执行规则,比如取款金额永远不能超过余额。

封装是如何工作的

封装结合了两个思想:

  1. 捆绑。 数据(余额)和操作数据的代码(存款、取款)放在同一个类里。
  2. 隐藏。 其他代码只能看到类的公有接口。接口背后的数据属于内部实现,只能以类允许的方式修改。

类所保护的规则叫作不变式(invariant),这里就是“余额永远不为负”。由于每一次修改都要经过类的方法,无论程序的其他部分做什么,这个不变式都始终成立。

70
refused: not enough money

外部代码可以读取 acct.balance,但这个属性没有 setter,所以 acct.balance = 1000 会抛出 AttributeError。如果余额出了错,需要检查的方法只有两个。

Python 中的封装

Python 没有 private 关键字。它依靠的是每个 Python 程序员都熟悉的命名约定:

  • name:公有,属于接口的一部分。
  • _name:内部使用。没有什么能阻止你读取它,但开头的下划线在告诉你“不要依赖它”。
  • __name:Python 会在类内部给它改名(名称改写),这样子类就不会意外覆盖它。
  • @property:把一个方法变成读起来像属性的东西,这样你以后可以加入检查,而无需修改任何调用代码。
0
False
42

名称改写防的是意外,而不是访问:改写后的名字照样能用。Python 相信程序员会尊重下划线的约定。实际上,大多数 Python 代码使用单下划线加 @property,只在专门用于继承的类中才使用双下划线。关于如何编写类,可以参阅 Python 类文档。

Java、C++ 和 JavaScript 中的封装

其他语言会强制执行隐藏。在 Java 中,private 字段只能在它所属的类内部使用:

public class BankAccount {
    private int balance;  // hidden from every other class

    public int getBalance() { return balance; }

    public void withdraw(int amount) {
        if (amount > balance) throw new IllegalArgumentException("not enough money");
        balance -= amount;
    }
}

在另一个类中写 account.balance = 1000; 无法通过编译:javac 会报告 balance has private access in BankAccount。

语言隐藏的成员公有的成员由谁强制执行
Python_name、__namename约定
Javaprivate int namepublic int name编译器
C++private: 部分public: 部分编译器
JavaScript#namenameJavaScript 引擎

C++ 中 class 的成员默认是私有的,struct 的成员默认是公有的。JavaScript 在 ES2022 中加入的 # 私有字段,在类外部完全无法读取。文档对这几种都有讲解:Java 访问修饰符、C++ 访问说明符和 JavaScript 私有字段。

封装为什么重要

  • 规则集中在一处。 “取款不能超过余额”这条检查只在 withdraw 里写一次,而不是在程序中每次取款之前都写一遍。
  • 内部可以随意修改。 如果你决定把金额从浮点数改为以“分”为单位的整数存储,只需修改这个类。调用 deposit(100) 的代码照常工作。
  • 调试更容易。 当某个值出错时,嫌疑对象只是少数几个有权修改它的方法,而不是程序中的每一行代码。
  • 需要学习和测试的东西更少。 一个有三个公有方法的类,通过这三个方法就能理解和测试。

封装也适用于比类更大的层面。一个只公开两个函数、把十个辅助函数留在内部的模块,也是同样意义上的封装;只允许程序通过系统调用来打开文件的操作系统也是如此。

封装经常和抽象混为一谈。抽象决定接口提供什么;封装保护接口背后的数据。抽象页面有一张完整的对比表。它们与多态和继承一起,构成了面向对象编程的四大特性。

常见错误

  • 给每个字段都配上 getter 和 setter。 一个接受任意数字的 setBalance 什么也保护不了。应该提供对象真正需要的操作(withdraw),而不是对每个字段的直接访问。
  • 返回内部对象。 一个返回 self._items 的方法,等于把真正的列表交给了调用方,调用方现在可以随意修改它。应该返回一个副本或元组。
  • 把私有当作安全手段。 访问修饰符防的是失误,不是攻击。Java 代码可以通过反射读取私有字段,Python 代码可以直接读取 _name。密码和密钥需要真正的保护措施,比如哈希和加密。

下一步学什么

接下来可以阅读抽象,了解这两个概念的区别,然后阅读多态。Python 课程会一步步带你编写类,Java 课程则展示了由编译器强制执行的封装。

常见问题

用简单的话说,封装是什么?
封装是指对象把自己的数据留在内部,只提供几种安全的使用方式。银行账户对象不允许你直接输入新的余额,而是提供 deposit 和 withdraw,它们会先检查规则。这个名字来自胶囊(capsule),也就是把内容物包在里面的外壳。
封装有哪些例子?
只允许通过 deposit 和 withdraw 修改余额的银行账户类是最经典的例子。Python 自带的 list 也是一个例子:你调用 append,却从来不用管理存放元素的内存。一个只公开几个函数、把辅助函数留作私有的模块,则是更大范围的封装。
封装有哪三种类型?

列出三种类型的教程通常指的是:成员变量封装(私有字段)、函数封装(私有辅助方法)和类封装(隐藏在另一个类或模块内部的类)。这些并不是正式的分类,只是把同一个思想分别应用到字段、方法和整个类上。

C++ 中的封装是什么,怎么用?
在 C++ 中,你把数据成员标记为 private,再提供 public 成员函数来读取或修改它们。class 的成员默认是私有的,而 struct 的成员默认是公有的。类外部任何试图使用私有成员的代码都会被编译器拒绝。
网络中的封装是什么意思?

在计算机网络中,封装是指数据在发出的过程中被逐层包上各个协议层的头部:HTTP 消息放进 TCP 段,TCP 段放进 IP 数据包,IP 数据包再放进以太网帧。它和编程中的封装同名,但是两个不同的概念。

Coddy 编程语言插图

和 Coddy 一起学编程

开始