访问修饰符

2003年5月13日

面向对象语言将程序划分为称为类的模块。每个类包含特性,特性由数据(字段)和方法组成。(并非所有语言都使用这些术语,但它们适用于此。)语言对其他类可以访问类的特性的规则有所不同,这些规则通常基于应用于类的访问修饰符。

C++ 选择

可能最具影响力的访问修饰符集始于 C++,它有三个。

  • public: 任何类都可以访问特性
  • protected: 任何子类都可以访问特性
  • private: 任何其他类都无法访问特性

类还可以使用friend关键字授予其他类或方法访问权限 - 因此有评论说在 C++ 中,朋友可以触碰彼此的私人物品。

Java

Java 基于 C++。它在语言中添加了包的概念,这影响了行为。

  • public: 任何类
  • (package): (默认值,在代码中不使用关键字)同一包中的任何类
  • protected: 任何子类或同一包中的类
  • private: 任何其他类

请注意 Java 的 protected 和 C++ 的 protected 之间的细微区别(只是为了让事情变得混乱)。

C#

C# 也基于 C++ 模型

  • public: 任何类
  • internal: 同一程序集中的任何类(方法和类的默认值,但可以指定)
  • protected: 任何子类
  • protected internal: 任何子类或同一程序集中的类
  • private: 任何其他类(字段的默认值)

在 C# 中,程序集是物理组合单元 - 等同于 dll、jar 或二进制文件。C# 还有逻辑单元(命名空间),类似于 Java 包,但它们不参与访问修饰符。

Smalltalk

Smalltalk 通常被认为是最纯粹的 OO 语言,并且早于 C++、Java 和 C#。它没有使用关键字来控制访问,而是使用基本策略。Smalltalk 程序员会说字段是私有的,方法是公开的。

但是,私有字段并不真正与它们在基于 C++ 的语言中的含义相同。在 C++ 等语言中,访问被认为是文本范围。考虑一个名为 Programmer 的类,它是名为 Person 的类的子类,有两个实例:Martin 和 Kent。在 C++ 中,由于这两个实例属于同一类,因此 Martin 可以访问 Kent 的私有特性。在 Smalltalk 的世界观中,访问基于对象,因此由于 Martin 和 Kent 是不同的对象,因此 Martin 不应该访问 Kent 的字段。但同样,由于一切都基于对象,因此 Martin 可以访问他所有的字段,即使它们是在 Person 类中声明的。因此,Smalltalk 中的数据更接近 protected 而不是 private,尽管对象范围在任何情况下都会使事情变得不同。

访问控制不控制访问

如果你有一个私有字段,这意味着任何其他类都无法访问它。错误!如果你真的想,你可以在几乎任何语言中破坏访问控制机制。通常,通过反射可以实现。其原理是调试器和其他系统工具通常需要查看私有数据,因此反射接口通常允许你这样做。

C++ 没有这种反射,但你可以直接使用内存操作,因为 C++ 本质上是开放内存的。

访问控制的意义不是阻止访问,而是更多地表明类希望将某些东西保留给自己。使用访问修饰符,就像编程中的许多事情一样,主要是关于沟通。

发布的方法

我曾认为,实际上还有另一种访问类型:PublishedInterface。我认为,你向项目团队中的其他类公开的特性与你向其他团队(例如在 API 中)公开的特性之间存在根本区别。这些发布的特性是公开特性的子集,必须以不同的方式对待,以至于我认为发布和公开之间的区别比公开和私有之间的区别更重要。