Java encapsulation

2022-09-01 23:58:06

我们总是说,如果我们简单地定义变量并定义getters setter来访问这些变量,那么数据将被封装。我的问题是,如果我们可以通过getter和setters访问变量(数据),那么为什么数据是隐藏的或安全的?private

我在谷歌上搜索了很多解释,但我什么也没找到。每个人都刚刚在他们的博客和帖子中说这是一种数据隐藏技术,但它还没有被解释/详细说明。


答案 1

封装不仅仅是为类定义访问器和赋值函数方法。它是面向对象编程的一个更广泛的概念,包括最小化类之间的相互依赖关系,并且通常通过信息隐藏来实现。

封装的美妙之处在于在不影响用户的情况下改变事物的力量

在像Java这样的面向对象的编程语言中,通过使用可访问性修饰符(公共,受保护,私有,加上没有修饰符(意味着包私有)隐藏细节来实现封装。通过这些级别的可访问性,您可以控制封装级别,该级别的限制越少,发生更改时的代价就越高,并且该类与其他依赖类(即用户类和子类)的耦合程度就越高。

因此,目标不是隐藏数据本身,而是隐藏有关如何操作此数据的实现细节。

这个想法是提供一个公共接口,通过该接口可以访问此数据。稍后可以更改数据的内部表示形式,而不会影响类的公共接口。相反,通过公开数据本身,您损害了封装,因此也损害了在不影响其用户的情况下更改数据操作方式的能力。创建对数据本身的依赖关系,而不是对类的公共接口的依赖关系。当“改变”最终找到你时,你会为麻烦创造一种完美的鸡尾酒。

您可能希望封装对字段的访问权限的原因有多种。Joshua Bloch 在他的著作《Effective Java》中,在 Item 14: Minimize the accessibility of classes and members 中提到了几个令人信服的原因,我在这里引用这些原因:

  • 您可以限制可以存储在字段中的值(即性别必须为 F 或 M)。
  • 您可以在修改字段时执行操作(触发事件、验证等)。
  • 您可以通过同步该方法来提供线程安全。
  • 您可以切换到新的数据表示形式(即计算字段,不同的数据类型)

但是,封装不仅仅是隐藏字段。在Java中,您可以隐藏整个类,通过这种方式,隐藏整个API的实现细节。例如,在方法中思考。它返回一个实现,但你不在乎哪个实现,只要它满足接口,对吧?将来可以更改实现,而不会影响方法的用户。Arrays.asList()ListList

封装之美

现在,在我看来,要理解封装,首先必须理解抽象。

例如,考虑汽车概念中的抽象层次。汽车的内部实现很复杂。它们有几个子系统,如传动系统,制动系统,燃油系统等。

但是,我们已经简化了它的抽象,我们通过抽象的公共界面与世界上所有的汽车进行交互。我们知道,所有的汽车都有一个方向盘,我们通过它来控制方向,它们有一个踏板,当你按下它时,你可以加速汽车并控制速度,另一个踏板,当你按下它时,你会让它停止,你有一个变速杆,让你控制你是前进还是后退。这些特性构成了汽车抽象的公共接口。早上,你可以开一辆轿车,然后下车,下午开一辆SUV,好像它是一回事。

但是,我们中很少有人知道如何在引擎盖下实现所有这些功能的细节。想想汽车没有液压定向系统的时代。有一天,汽车制造商发明了它,他们决定从那里开始把它放在汽车里。尽管如此,这并没有改变用户与他们互动的方式。最多,用户在使用定向系统方面经历了改进。像这样的变化是可能的,因为汽车的内部实现是封装的。可以安全地进行更改,而不会影响其公共接口。

现在,想想汽车制造商决定将燃油盖放在汽车下方,而不是放在汽车的一侧。你去买一辆这样的新车,当你用完汽油时,你去加油站,你找不到燃料盖。突然间,你意识到它在汽车下面,但你无法用气泵软管到达它。现在,我们已经打破了公共接口契约,因此,整个世界都崩溃了,它分崩离析,因为事情没有按照预期的方式工作。这样的变化将花费数百万美元。我们需要改变世界上所有的加油泵。当我们打破封装时,我们必须付出代价。

因此,如您所见,封装的目标是最大限度地减少相互依赖并促进更改。通过最大限度地减少实现细节的公开,可以最大限度地提高封装。类的状态只能通过其公共接口进行访问。

我真的建议你阅读Alan Snyder的一篇论文,名为《面向对象编程语言中的封装和继承》。这个链接指向ACM的原始论文,但我非常确定你能够通过Google找到PDF副本。


答案 2

我理解你的问题是,尽管我们将变量声明为 ,因为这些变量可以使用 getter 和 setter 访问,但它们不是私有的。因此,这样做的意义是什么?private

好吧,当使用 getter 和 setter 时,你可以限制对变量的访问。private

private int x;

public int getInt(String password){
 if(password.equals("RealPassword")){
   return x;
  }
}

二传手也是如此。