简体   繁体   English

为什么Java中的实例变量总是私有的?

[英]Why are instance variables in Java always private?

I'm newbie to Java and I'm learning about encapsulation and saw an example where instance variables are declared as private in a class. 我是Java的新手,我正在学习封装,并看到了一个示例,其中实例变量在类中声明为私有。

http://www.tutorialspoint.com/java/java_encapsulation.htm http://www.tutorialspoint.com/java/java_encapsulation.htm

I have 2 queries: 我有2个查询:

  1. Why are instance variables private? 为什么实例变量是私有的? Why not public? 为什么不公开?
  2. What if instance variables are made public and accessed directly? 如果实例变量公开并直接访问会怎样? Do we see any constraints? 我们看到任何限制吗?

Can you explain with an example as to what will go wrong in case the instance variables are declared as public in a class in Java? 你可以用一个例子来解释,如果在Java中的类中将实例变量声明为public,会出现什么问题?

Instance variables are made private to force the users of those class to use methods to access them. 实例变量是私有的,以强制这些类的用户使用方法来访问它们。 In most cases there are plain getters and setters but other methods might be used as well. 在大多数情况下,有普通的getter和setter,但也可以使用其他方法。

Using methods would allow you, for instance, to restrict access to read only, ie a field might be read but not written, if there's no setter. 例如,使用方法可以限制只读访问,即如果没有setter,则可以读取字段但不写入字段。 That would not be possible if the field was public. 如果该领域是公开的,这是不可能的。

Additionally, you might add some checks or conversions for the field access, which would not be possible with plain access to a public field. 此外,您可以为字段访问添加一些检查或转换,这对于公共字段的普通访问是不可能的。 If a field was public and you'd later like to force all access through some method that performs additional checks etc. You'd have to change all usages of that field. 如果某个字段是公开的,并且您以后想要通过执行其他检查等的某种方法强制所有访问。您必须更改该字段的所有用法。 If you make it private, you'd just have to change the access methods later on. 如果您将其设为私有,则只需稍后更改访问方法即可。

If phone was private: 如果phone是私密的:

Consider this case: 考虑这种情况:

class Address {
  private String phone;

  public void setPhone(String phone) {
    this.phone = phone;
  }
}

//access:
Address a = new Address();
a.setPhone("001-555-12345");

If we started with the class like this and later it would be required to perform checks on the phoneNumber (eg some minimum length, digits only etc.) you'd just have to change the setter: 如果我们从这个类开始,之后需要对phoneNumber执行检查(例如,一些最小长度,仅数字等),您只需要更改setter:

class Address {
  private String phone;

  public void setPhone(String phone) {
    if( !isValid( phone) ) { //the checks are performed in the isValid(...) method
     throw new IllegalArgumentException("please set a valid phone number");
    }

    this.phone = phone;
  }
}

//access:
Address a = new Address();
a.setPhone("001-555-12345"); //access is the same

If phone was public: 如果phone是公开的:

Someone could set phone like this and you could not do anything about it: 有人可以设置这样的phone ,你无法做任何事情:

Address a = new Address();
a.phone="001-555-12345";

If you now want to force the validation checks to be performed you'd have to make it private and whoever wrote the above lines would have to change the second line to this: 如果您现在想要强制执行验证检查,则必须将其设为私有,并且无论是谁编写上述行都必须将第二行更改为:

a.setPhone("001-555-12345");

Thus you couldn't just add the checks without breaking other code (it wouldn't compile anymore). 因此,您不能在不破坏其他代码的情况下添加检查(它将不再编译)。

Additionally, if you access all fields/properties of a class through methods you keep access consistent and the user would not have to worry about whether the property is stored (ie is a instance field) or calculated (there are just methods and no instance fields). 此外,如果通过方法访问类的所有字段/属性,则保持访问一致,用户不必担心属性是存储(即是实例字段)还是计算(只有方法而没有实例字段) )。

They don't have to be private - but they should be. 他们没有为私有-但他们应该。 A field is an implementation detail - so you should keep it private. 字段是实现细节 - 因此您应该将其保密。 If you want to allow users to fetch or set its value, you use properties to do so (get and set methods) - this lets you do it safely (eg validating input) and also allows you to change the implementation details (eg to delegate some of the values to other objects etc) without losing backward compatibility. 如果您想允许用户获取或设置其值,您可以使用属性来执行此操作(获取和设置方法) - 这使您可以安全地执行此操作(例如验证输入)并且还允许您更改实现详细信息(例如,委托其他对象的一些值等),而不会失去向后兼容性。

First, it is not true that all instance variables are private. 首先,并非所有实例变量都是私有的。 Some of them are protected, which still preserves encapsulation. 其中一些是受保护的,仍然保留封装。

The general idea of encapsulation is that a class should not expose its internal state. 封装的一般思想是类不应该暴露其内部状态。 It should only use it for performing its methods. 它应该只用它来执行它的方法。 The reason is that each class has a so-called "state space". 原因是每个班级都有一个所谓的“国家空间”。 That is, a set of possible values for its fields. 也就是说,其字段的一组可能值。 It can control its state space, but if it exposes it, others might put it in an invalid state. 它可以控制其状态空间,但如果它暴露它,其他人可能会将其置于无效状态。

For example, if you have two boolean fields, and the class can function properly only in 3 cases: [false, false], [false, true], and [true, false]. 例如,如果您有两个布尔字段,并且该类只能在3种情况下正常运行:[false,false],[false,true]和[true,false]。 If you make the fields public, another object can set [true, true], not knowing the internal constraints, and the next method called on the original object will trigger unexpected results. 如果将字段设置为public,则另一个对象可以设置[true,true],不知道内部约束,并且调用原始对象的下一个方法将触发意外结果。

Making instance variables public or private is a design tradeoff the designer makes when declaring the classes. 将实例变量设为公共或私有是设计者在声明类时所做的设计权衡。 By making instance variables public, you expose details of the class implementation, thereby providing higher efficiency and conciseness of expression at the possible expense of hindering future maintenance efforts. 通过公开实例变量,您可以公开类实现的细节,从而提供更高的效率和简洁的表达,但可能会妨碍将来的维护工作。 By hiding details of the internal implementation of a class, you have the potential to change the implementation of the class in the future without breaking any code that uses that class. 通过隐藏类的内部实现的细节,您有可能在将来更改类的实现,而不会破坏使用该类的任何代码。

Oracle White Paper Oracle白皮书

Like has been pointed out by several answerers already, instance variables don't have to be private , but they are usually at the very least not made public , in order to preserve encapsulation. 正如已经由几个回答者指出的那样,实例变量不必是private ,但为了保持封装,它们通常至少不public

I saw an example in (I think) Clean Code, which very well illustrates this. 我在(我认为)清洁代码中看到了一个例子,它很好地说明了这一点。 If I recall correctly, it was a complex number (as in a+bi ) type; 如果我没记错的话,这是一个复数(如a+bi )类型; in any case, something very much like that, I don't have the book handy. 在任何情况下,非常类似的东西,我没有这本书方便。 It exposed methods to get the value of the real and imaginary parts as well as a method to set the value of the instance. 它暴露了获取实部和虚部的值的方法以及设置实例值的方法。 The big benefit of this is that it allows the implementation to be completely replaced without breaking any consumers of the code. 这样做的最大好处是它允许在不破坏任何代码消费者的情况下完全替换实现。 For example, complex numbers can be stored on one of two forms: as coordinates on the complex plane ( a+bi ), or in polar form ( φ and |z| ). 例如,复数可以以两种形式之一存储:作为复平面上的坐标( a+bi ),或者以极坐标形式( φ|z| )。 Keeping the internal storage format an implementation detail allows you to change back and forth while still exposing the number on both forms, thus letting the user of the class pick whichever is more convenient for the operation they are currently performing. 保持内部存储格式的实现细节允许您在仍然暴露两个表单上的数字时来回切换,从而让类的用户选择更方便他们当前执行的操作。

In other situations, you may have a set of related fields, such as field x must have certain properties if field y falls inside a given range. 在其他情况下,您可能有一组相关字段,例如字段x必须具有某些属性,如果字段y落在给定范围内。 A simplistic example would be where x must be in the range y through y+z , for numerical values and some arbitrary value z . 一个简单的例子是x必须在yy+z的范围内,对于数值和一些任意值z By exposing accessors and mutators, you can enforce this relationship between the two values; 通过公开访问器和更改器,您可以在两个值之间强制执行此关系; if you expose the instance variables directly, the invariant falls apart immediately, since you cannot guarantee that someone won't set one but not the other, or set them so that the invariant no longer holds. 如果直接暴露实例变量,则不变量会立即崩溃,因为您无法保证某人不会设置一个而不会设置另一个,或者设置它们以使不变量不再成立。

Of course, considering reflection, it's still possible to access members you aren't supposed to, but if someone is reflecting your class to access private members, they had better realize that what they are doing may very well break things. 当然,考虑到反思,仍然可以访问您不应该访问的成员,但如果有人反映您的课程以访问私人成员,他们最好意识到他们正在做的事情可能会破坏事情。 If they are using the public interface, they might think everything is fine, and then they end up with nasty bugs because they unknowingly did not fully adhere to the implementation details of your particular implementation. 如果他们使用公共界面,他们可能会认为一切都很好,然后他们最终会遇到令人讨厌的错误,因为他们在不知不觉中没有完全遵守您特定实现的实现细节。

In traditional Object-Oriented design, a class will encapsulate both data (variables) and behavior (methods). 在传统的面向对象设计中,类将封装数据(变量)和行为(方法)。 Having private data will give you flexibility as to how the behavior is implemented, so for example, an object could store a list of values and have a getAverage() method that computes and returns the mean of these values. 拥有私有数据将为您提供关于如何实现行为的灵活性,因此,例如,对象可以存储值列表并具有计算并返回这些值的平均值的getAverage()方法。 Later on, you could optimize and cache the computed average in the class, but the contract (ie, the methods) would not need to change. 稍后,您可以优化并缓存类中的计算平均值,但合同(即方法)不需要更改。

It has become more popular the past few years (for better or worse) to use anemic data models , where a class is nothing but a bunch of fields and corresponding getters and setters. 在过去的几年里(无论好坏),使用贫血数据模型变得越来越流行,其中一个类只不过是一堆字段和相应的getter和setter。 I would argue that in this design you would be better off with public fields, since the getters and setters provide no real encapsulation, but just fool you into thinking you are doing real OO. 我认为在这个设计中你会更好地使用公共领域,因为getter和setter没有提供真正的封装,但只是愚弄你以为你在做真正的OO。

UPDATE: The example given in the link in the question is a perfect example of this degenerate encapsulation. 更新:问题中链接中给出的示例是这种简并封装的完美示例。 I realize the author is trying to provide a simple example, but in doing so, fails to convey any real benefit of encapsulation (at least not in the example code). 我意识到作者试图提供一个简单的例子,但是这样做无法传达封装的任何真正好处(至少在示例代码中没有)。

Well keeping fields private has many advantages as suggested above. 保持领域私有具有如上所述的许多优点。 Next best level is to keep them package private using java default access level. 下一个最佳级别是使用java默认访问级别将它们保密。

Default level avoid cluttering in your own code and prevents clients of your code from setting invalid values. 默认级别可以避免在您自己的代码中出现混乱,并防止代码的客户端设置无效值。

For user of class 对于班级用户

We, who are using ide like eclipse, netbins..... saw that it suggest us for public method, so if creator of class provide getter and setter for private instance variable you do not have to memorize the name of variable. 我们正在使用像eclipse这样的ide,netbins .....看到它建议我们使用公共方法,所以如果类的创建者为私有实例变量提供getter和setter,则不必记住变量的名称。 just write set press ctrl+space you are getting all of setter method created by creator of that class and choose your desired method to set your variable value. 只需编写set press ctrl + space,即可获得该类创建者创建的所有setter方法,并选择所需的方法来设置变量值。

For creator of class 对于班级的创造者

Sometimes you need to specify some logic to set variable value. 有时您需要指定一些逻辑来设置变量值。 "suppose you have an integer variable which should store 0 “假设你有一个应该存储0的整数变量

  1. Because if you change the structure of the class (removing fields etc.); 因为如果你改变了类的结构(删除字段等); it will cause bugs. 它会导致错误。 But if you have a getX() method you can calculate the needed value there (if field was removed). 但是如果你有一个getX()方法,你可以在那里计算所需的值(如果删除了字段)。

  2. You have the problem then that the class does not know if something is changed and can't guarantee integrity. 您遇到的问题是,该类不知道某些内容是否已更改且无法保证完整性。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM