对象是否封装数据,以至于即使是同一类的其他实例也无法访问数据?

7

在Java中,对象是否封装数据以使得即使是同类的其他实例也无法访问该数据?只有使用关键字"private"时才可以吗?在Java中,什么是"访问器方法" - 例如getName()这样的方法?

谢谢

7个回答

9

我不倾向于认为一个对象可以访问另一个对象,而是认为什么代码可以访问对象中的什么数据。

在Java(以及C#)中,类内部的代码可以访问同一类的任何对象的私有成员。然后你还有包/程序集访问和公共访问。

棘手的是受保护的访问权限,这是对子类中的代码的某种程度的访问权限 - 但这取决于目标对象:只有当目标对象是与代码位置相同类型或某个子类的实例时,才允许访问对象的受保护成员,即使它被父类暴露出来。因此,例如,假设你有:

class Parent
{
    protected int x;
}

class Child1 extends Parent
class Child2 extends Parent
class Grandchild extends Child1

Child1代码内部,您只能访问已知(在编译时)为Child1Grandchild实例的对象的Parent.x。例如,您不能使用new Parent().xnew Child2().x


4

不可以,私有字段甚至可以从其他实例(在同一类的方法中)访问。

然而,它们无法从子类访问,即使是在同一个实例内也不行。

您提供getter方法以允许“外部”代码访问类中的字段。由于您决定提供哪些getter、将其设置为多少可见,并如何实现它们,因此您可以对谁可以访问数据以及如何访问数据进行控制。

请注意,如果存在getName,则实际上不需要name字段:获取器的实现方式完全取决于获取器。

即使getter(或setter)只包装了一个私有字段,使用这些setter和getter(而不是允许直接访问字段)也是很好的风格。


1

getName() 应该返回名称(无论是字段还是其他“东西”)。


好的,它应该返回名称。无论是保存在名为“name”的字段中还是从其他对象中派生出来的,都是实现细节。 - Jon Skeet

1
即使一个字段/方法是“私有的”,它仍然可以通过反射访问/调用,除非您安装了一个自定义安全管理器来禁止这样做。

你也可以通过JNI访问字段。谁在乎呢?(而且,安全管理器无需定制。) - Tom Hawtin - tackline
是的,没错。这就是尽可能避免使用反射的原因之一。 - sleske

1
封装的思想是允许不同单元的实现自由变化。虽然我们谈论的是对象,但对于封装,我们真正意义上指的是代码单元。在基于类的语言中,代码单元通常是[外部]类。
同时,二进制操作(例如equals)如果没有访问权限,就会变得愚蠢。因此,private表示仅限于[外部]类而不是在同一实例中相同类内部的private。
访问器方法通常除了简单值对象(仅有getter)外,表明设计不佳。对象应该具有行为,而不仅仅是数据的哑集合。将使用getter从外部获取的代码移动到对对象有意义的方法中。说实话,99%的时候getter只是返回字段值。如果您要添加getter和setter,则使字段私有化的价值相对较小。

0
在Java中,“访问器方法”是什么 - 像getName()这样的方法?
是的 - getFoo()和setFoo()是名为foo的“属性”的访问器方法 - 这是JavaBeans规范的一部分。之所以比公共字段更受欢迎,是因为它们允许您仅拥有getter(使属性只读),进行额外的簿记(例如计算派生字段)和验证设置值(当值不可接受时抛出PropertyVetoException)。
整个概念最初旨在与GUI工具一起使用,这些工具将允许您以图形方式配置和组合JavaBeans以“构建应用程序”。这被证明在很大程度上是一个白日梦,但JavaBeans和属性的概念已经被证明对于常规编码非常有用并且变得广泛使用。
许多人误解了封装的概念,认为“封装”只是编写私有属性的setter和getter而不是将它们公开 - 然后理所当然地认为这很愚蠢。封装意味着除了在严格受控的方式下之外,根本不暴露类的内部工作原理。在良好的面向对象设计中,类中不应该有太多的get方法和非常少的set方法。

0

对象是否封装数据,以至于即使是同一类的其他实例也无法访问该数据?

当然可以,如果您没有使用静态成员。

此链接中提取:

有时,您希望拥有对所有对象都通用的变量。这可以通过使用 static 修饰符来实现。在其声明中具有 static 修饰符的字段称为静态字段或类变量。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接