java Java封装

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/11966763/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-31 07:06:45  来源:igfitidea点击:

Java encapsulation

javaoopjakarta-eeencapsulation

提问by Just_another_developer

We always say that data will be encapsulated if we simply define variables privateand define getters setters to access those variables. My question is if we can access the variables (data) though via getters and setters, how come data is hidden or safe?

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

I googled a lot for an explanation, but I found nothing. Every one just said in their blogs and posts that it is a data hiding technique, but it has not been explained/elaborated.

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

采纳答案by mithilatw

The way I understand your question is, although we declare variables as private, as those variables can be accessed using getters and setters, they are not private. Therefore, what is the meaning of doing that?

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

Well, when using getters and setters, you can restrict access to the privatevariables.

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

I.e.,

IE,

private int x;

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

And the same for the setters.

对于二传手也是如此。

回答by Edwin Dalorzo

Encapsulation is more than just defining accessor and mutator methods for a class. It is a broader concept of object-oriented programming that consists in minimizing the interdependence between classes and it is typically implemented through information hiding.

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

The beauty of encapsulation is the power of changing things without affecting its users.

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

In an object-oriented programming language like Java, you achieve encapsulation by hiding details using the accessibility modifiers (public, protected, private, plus no modifier which implies package private). With these levels of accessibility, you control the level of encapsulation, the less restrictive the level, the more expensive change is when it happens and the more coupled the class is with other dependent classes (i.e. user classes and subclasses).

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

Therefore, the goal is not to hide the data itself, but the implementation details on how this data is manipulated.

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

The idea is to provide a public interface through which you gain access to this data. You can later change the internal representation of the data without compromising the public interface of the class. On the contrary, by exposing the data itself, you compromise encapsulation, and therefore, the capacity of changing the way you manipulate the data without affecting its users. You create a dependency on the data itself, and not with the public interface of the class. You would be creating a perfect cocktail for trouble when "change" finally finds you.

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

There are several reasons why you might want to encapsulate access to your fields. Joshua Bloch in his book Effective Java, in Item 14: Minimize the accessibility of classes and members, mentions several compelling reasons, which I quote here:

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

  • You can limit the values that can be stored in a field (i.e. gender must be F or M).
  • You can take actions when the field is modified (trigger event, validate, etc.).
  • You can provide thread safety by synchronizing the method.
  • You can switch to a new data representation (i.e. calculated fields, different data type)
  • 您可以限制可以存储在字段中的值(即性别必须是 F 或 M)。
  • 您可以在字段被修改时采取行动(触发事件、验证等)。
  • 您可以通过同步方法来提供线程安全性。
  • 您可以切换到新的数据表示(即计算字段、不同的数据类型)

However, encapsulation is more than hiding fields. In Java you can hide entire classes, by this, hiding the implementation details of an entire API. Think, for example, in the method Arrays.asList(). It returns a Listimplementation, but you do not care which implementation, as long as it satisfies the Listinterface, right?. The implementation can be changed in the future without affecting the users of the method.

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

The Beauty of Encapsulation

封装之美

Now, in my opinion, to really understand encapsulation, one must first understand abstraction.

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

Think, for example, in the level of abstraction in the concept of a car. A car is complex in its internal implementation. They have several subsystems, like a transmission system, a brake system, a fuel system, etc.

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

However, we have simplified its abstraction, and we interact with all cars in the world through the public interface of their abstraction. We know that all cars have a steering wheel through which we control direction, they have a pedal that when you press it you accelerate the car and control speed, and another one that when you press it you make it stop, and you have a gear stick that let you control if you go forward or backward. These features constitute the public interface of the car abstraction. In the morning you can drive a sedan and then get out of it and drive an SUV in the afternoon as if it was the same thing.

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

However, few of us know the details of how all these features are implemented under the hood. Think of the time when cars did not have a hydraulics directional system. One day, the car manufactures invented it, and they decide it to put it in cars from there on. Still, this did not change the way in which users were interacting with them. At most, users experienced an improvement in the use of the directional system. A change like this was possible because the internal implementation of a car is encapsulated. Changes can be safely done without affecting its public interface.

然而,我们中很少有人知道所有这些功能是如何在幕后实现的。想想汽车没有液压方向系统的时代。有一天,汽车制造商发明了它,他们决定从那时起将其放入汽车中。尽管如此,这并没有改变用户与他们互动的方式。最多,用户在使用定向系统时体验到了改进。像这样的改变是可能的,因为汽车的内部实现是封装的。可以安全地进行更改,而不会影响其公共接口。

Now, think that car manufacturers decided to put the fuel cap below the car, and not in one of its sides. You go and buy one of these new cars, and when you run out of gas you go to the gas station, and you do not find the fuel cap. Suddenly you realize it's below the car, but you cannot reach it with the gas pump hose. Now, we have broken the public interface contract, and therefore, the entire world breaks, it falls apart because things are not working the way it was expected. A change like this would cost millions. We would need to change all gas pumps in the world. When we break encapsulation we have to pay a price.

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

So, as you can see, the goal of encapsulation is to minimize interdependence and facilitate change. You maximize encapsulation by minimizing the exposure of implementation details. The state of a class should only be accessed through its public interface.

因此,如您所见,封装的目标是最小化相互依赖并促进变化。您可以通过最小化实现细节的暴露来最大化封装。类的状态只能通过其公共接口访问。

I really recommend you to read a paper by Alan Snyder called Encapsulation and Inheritance in Object-Oriented Programming Languages. This link points to the original paper on ACM, but I am pretty sure you will be able to find a PDF copy through Google.

我真的建议您阅读 Alan Snyder 的一篇名为Encapsulation and Inheritance in Object-Oriented Programming Languages 的论文。此链接指向 ACM 上的原始论文,但我很确定您可以通过 Google 找到 PDF 副本。

回答by markus

The data is safe, because you can do additional logic in your getter / setter, and it's not possible to change the values of your variable. Imagine your code did not work with a null variable, so in your setter you can check against null values and assign a default value which is != null. So your code is still working, regardless of whether someone tries to set your variable to null.

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

回答by Jigar Joshi

My question is if we can access the variables (data) though via getters and setters, how come data is hidden or safe?

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

You can encapsulate the logic under getters/setters. For example,

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

public void setAge(int age) {
    if (age < 0) {
        this.age = 0;
    }
    else {
        this.age = age;
    }
}

回答by Nishant

Continuing Jigar's answer: There are a couple of things that come under encapsulation.

继续 Jigar 的回答:有几件事需要封装。

  1. Contract Management:If you make it public, you are literally making anyone to change it to whatever they want. You can't protect it by adding a constraint. Your setter can make sure data gets modified in an appropriate way.

  2. Mutability:You do not always have to have a setter. If there is a property that you wanted to keep immutable for the life span of the object. You just make it private and have no setter for it. It will probably be set via constructor. Then your getter will just return the attribute (if it's immutable) or a copy of the attribute (if attribute is mutable).

  1. 合同管理:如果您做到了public,您实际上就是在让任何人将其更改为他们想要的任何内容。您无法通过添加约束来保护它。您的 setter 可以确保以适当的方式修改数据。

  2. 可变性:你并不总是必须有一个二传手。如果您希望在对象的生命周期内保持不可变的属性。您只需将其设为私有并且没有设置者即可。它可能会通过构造函数设置。然后你的 getter 将只返回属性(如果它是不可变的)或属性的副本(如果属性是可变的)。

回答by Markus Kreusch

In general, encapsulation of fields by getters and setters leaves more flexibility for changes.

一般来说,getter 和 setter 对字段的封装为更改留下了更大的灵活性。

If fields are accessed directly you are stuck with "stupid fields". Fields can only be written and read. Nothing else can be done while accessing a field.

如果直接访问字段,您就会陷入“愚蠢的字段”。字段只能写入和读取。访问字段时无法执行任何其他操作。

When using methods you can do whatever you want while setting / reading the value. As markus and Jigar mentioned, validation is possible. In addition, you could decide some day that the value is derived from another value or some actions must be executed if the value changes.

使用方法时,您可以在设置/读取值时做任何想做的事情。正如 markus 和 Jigar 所提到的,验证是可能的。此外,您可以决定某天该值是从另一个值派生的,或者如果该值发生变化则必须执行某些操作。

how come data is hidden or safe

为什么数据是隐藏或安全的

Data is neither hidden nor safe by using getters and setters. It just gives you the possibility to make it safe. What is hidden is the implementation and not the data.

使用 getter 和 setter 既不隐藏也不安全。它只是让您有可能使其安全。隐藏的是实现而不是数据。

回答by Grim0013

Data validation is the main answer to your question regarding how encapsulation provides safety if there are accessors and/or mutators in play. Others have mentioned this using examples of having a fail-safe to set a default value in the mutator. You replied that you prefer throwing exceptions instead, which is great, but identifying that you have bad data when you go to use it does not change the fact that you have bad data. Would it therefore not be best to catch the exception beforedata is modified, i.e., do it in the mutator? This way the actual data is never modified unless the mutator has verified it as being valid, thus the original data is preserved in the event of bad data.

如果存在访问器和/或修改器,则数据验证是关于封装如何提供安全性的问题的主要答案。其他人使用具有故障安全功能的示例来提到这一点,以在 mutator 中设置默认值。你回答说你更喜欢抛出异常,这很好,但是当你去使用它时发现你有坏数据并不会改变你有坏数据的事实。因此,修改数据之前捕获异常不是最好的,即在 mutator 中进行吗?通过这种方式,除非修改器已验证其有效,否则永远不会修改实际数据,从而在出现错误数据时保留原始数据。

I'm still just a student myself, but I had the same exact though as you when I first encountered encapsulation, so I spent some time figuring it out.

我自己也只是一个学生,但我第一次遇到封装时和你一样,所以我花了一些时间弄清楚。

回答by Eugene

I like the explanation when taking into consideration threads. If you made your fields public, how is your instance going to know when a certain thread changed one of its fields?

我喜欢考虑线程时的解释。如果您公开了您的字段,您的实例如何知道某个线程何时更改了它的一个字段?

The only way to do it would be with encapsulation, or simpler put give getters and setters to that field, thus you alwaysknow and can check/react to fields updates, for example.

唯一的方法是使用封装,或者更简单的 put 为该字段提供 getter 和 setter,因此您总是知道并且可以检查/响应字段更新,例如。

回答by blockay

Encapsulation makes code more easy to reuse by other people. Another key reason to use encapsulation is that interfaces can not declare fields, but they can declare methods, which can refer to a field!

封装使代码更容易被其他人重用。使用封装的另一个关键原因是接口不能声明字段,但可以声明方法,方法可以引用字段!

Methods are properly named with a verb at the beginning. Such as: getName(), setName(),isDying(). Which can help read code!

方法在开头用动词正确命名。如:getName()、setName()、isDying()。哪位能帮忙看代码!