Java封装

Jus*_*per 21 java oop encapsulation java-ee

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

我搜索了大量的解释但没有找到任何东西.每个人都在他们的博客和帖子中说它是一种数据隐藏技术,但没有解释/阐述它.

期待在stackoverflow论坛上获得适当,满意的解释.

Edw*_*rzo 51

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

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

在像Java这样的面向对象编程语言中,您可以通过使用辅助功能修饰符(public,protected,private,以及暗示包private的修饰符)来隐藏细节来实现封装.通过这些级别的可访问性,您可以控制封装级别,级别限制越少,发生更改的代价越高,类与其他依赖类(即用户类,子类)的耦合程度越高.

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

我们的想法是提供一个公共接口,通过它您可以访问这些数据.您可以稍后更改数据的内部表示,而不会影响类的公共接口.相反,通过暴露数据本身,您可以破坏封装,从而改变操作数据的方式而不影响其用户的能力.您创建了对数据本身的依赖关系,而不是该类的公共接口.当"改变"最终找到你时,你会创造一个完美的鸡尾酒.

您可能希望封装对字段的访问权限的原因有多种.Joshua Bloch在他的"有效Java"一书中,第14项:最小化课程和成员的可访问性,提到了几个令人信服的理由,我在这里引用:

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

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

封装之美

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

例如,考虑一下汽车概念的抽象层次.汽车内部实施复杂.它们有几个子系统,如传动系统,制动系统,燃料系统等.

然而,我们已经简化了它的抽象,我们通过它们的抽象的公共接口与世界上的所有汽车进行交互.我们知道所有的汽车都有一个方向盘,我们通过它来控制方向,他们有一个踏板,当你按下它时,你加速汽车和控制速度,另一个,当你按下它,你让它停止,你有一个齿轮如果你前进或后退,你可以控制它.这些特征构成了汽车抽象的公共界面.早上你可以驾驶一辆轿车然后下车并在下午驾驶一辆SUV,好像它是一样的.

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

现在,我认为汽车制造商决定将燃油盖放在汽车下方,而不是其中一侧.你去购买这些新车中的一辆,当你用完汽油时,你会去加油站,而你却找不到加油盖.突然你意识到它在汽车下方,但你无法用气泵软管到达它.现在,我们打破了公共接口合同,因此整个世界都破裂了,它因为事情没有按照预期的方式运转而崩溃.这样的改变会花费数百万美元.我们需要改变世界上所有的气泵.当我们打破封装时,我们必须付出代价.

因此,正如您所看到的,封装的目标是最小化相互依赖性并促进变更.您可以通过最小化实现细节的曝光来最大化封装.只能通过其公共接口访问类的状态.

我真的建议你阅读Alan Snyder撰写的一篇论文,称为面向对象编程语言中的封装和继承.此链接指向ACM上的原始论文,但我相信您将能够通过Google找到PDF副本.

  • 很好的回应.特别是当你在这3行中总结你的评论时"所以,正如你所看到的,封装的目标是最小化相互依赖性并促进变化.通过最小化实现细节的暴露来最大化封装.类的状态应该只是通过其公共接口访问." .. 不错的工作! (8认同)
  • 哇.很棒的答案! (2认同)
  • @oblivion 你可能还喜欢阅读【抽象、封装和信息隐藏】(http://www.tonymarston.co.uk/php-mysql/abstraction.txt) (2认同)

mit*_*atw 15

我理解你的问题的方式是,虽然我们将变量声明为private,因为这些变量可以使用getter和setter来访问,但它们不是私有的.那么,这样做的意义是什么?

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

private int x;

public int getInt(String password){
 if(password.equals("RealPassword")){
   return x;
  }
}
Run Code Online (Sandbox Code Playgroud)

对于制定者来说也是如此.希望这可以帮助!

  • (没有/我没有听说过)这样的惯例是不要将参数值传递给吸气剂!(它们是参数值而不是显式值)并且就设置器而言,您可以将多个参数传递给方法. (3认同)

mar*_*kus 13

数据是安全的,因为您可以在getter/setter中执行其他逻辑,并且无法更改变量的值.想象一下,您的代码无法使用null变量,因此在您的setter中,您可以检查空值并指定一个默认值!= null.因此,无论是否有人试图将您的变量设置为null,您的代码仍然有效.


Jig*_*shi 5

我的问题是,如果我们可以通过getter和setter访问变量(数据),那么数据是如何隐藏或安全的?

例如,您可以在getter/setter下封装逻辑

public void setAge(int age) {
    if (age < 0) {
        this.age = 0;
    }
    else {
        this.age = age;
    }
}
Run Code Online (Sandbox Code Playgroud)