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

Posted

技术标签:

【中文标题】为啥Java中的实例变量总是私有的?【英文标题】:Why are instance variables in Java always private?为什么Java中的实例变量总是私有的? 【发布时间】:2011-11-29 04:18:51 【问题描述】:

我是 Java 新手,我正在学习封装,并看到了一个示例,其中实例变量在类中声明为私有。

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

我有 2 个查询:

    为什么实例变量是私有的?为什么不公开? 如果将实例变量公开并直接访问会怎样?我们是否看到任何限制?

您能否举例说明如果实例变量在 Java 中的类中声明为 public 会出现什么问题?

【问题讨论】:

你能用一个例子解释一下如果实例变量在 Java 的类中被声明为 public 会出现什么问题吗? 如果一个字段永远不应该为空的图像。如果你有一个 setter 方法,你可以检查 null 和 throw 和 IllegalArgumentException 如果 null 曾经通过。但是,如果该字段是public,那么您班级的任何用户都可以将该字段设置为任何值,包括null。然后,您的代码可能会得到 NullPointerException,因为它总是希望该字段永远不会是 null 您发布的链接末尾有一个“封装的好处”部分,这对查看很有用。 他们并不总是私密的。通常是这样,但并非总是如此。 【参考方案1】:

实例变量被设为私有以强制这些类的用户使用方法来访问它们。 在大多数情况下,有简单的 getter 和 setter,但也可以使用其他方法。

例如,使用方法将允许您将访问限制为只读,即如果没有设置器,则可以读取但不写入字段。如果该字段是公开的,那将是不可能的。

此外,您可以为字段访问添加一些检查或转换,这在普通访问公共字段时是不可能的。如果一个字段是公开的,并且您稍后希望通过某种执行额外检查等的方法强制所有访问。您必须更改该字段的所有用法。如果您将其设为私有,您只需稍后更改访问方法。

如果 phone 是私有的:

考虑这种情况:

class Address 
  private String phone;

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


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

如果我们从这样的类开始,然后需要对 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

如果 phone 是公开的:

有人可以这样设置phone,而你对此无能为力:

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

如果您现在想强制执行验证检查,您必须将其设为私有,并且编写上述行的人必须将第二行更改为:

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

因此,您不能只添加检查而不破坏其他代码(它不会再编译)。

此外,如果您通过方法访问类的所有字段/属性,您可以保持访问一致,用户不必担心属性是存储的(即实例字段)还是计算的(只有方法和没有实例字段)。

【讨论】:

Thomas,你能回顾一下你的例子吗?因为我认为第一个代码 sn-p 中的字符串电话应该是公开的。!!! 对我来说看起来还不错,在评论 if phone was public 中有警告。 @Deepak 我重新格式化了答案以明确条件:该示例显示了它应该是什么样子(phone 是私有的)并且每个块的最后一部分应该显示如果它是可能会发生什么改为公开。 >如果该字段是公开的,这是不可能的。 - final 修饰符怎么样? @SargeBorsch 您可以将public final 字段用作只读常量,但在许多情况下该字段不是常量。通常只读只会影响界面,即对象的用户不能修改字段。然而,在内部,对象可以更新字段,例如通过进行一些计算,这对于 final 字段是不可能的。【参考方案2】:

他们没有必须保密 - 但他们应该这样做。一个字段是一个实现细节 - 所以你应该把它保密。如果您想允许用户获取或设置其值,您可以使用 properties 来执行此操作(get 和 set 方法) - 这可以让您安全地执行此操作(例如验证输入)并允许您更改实现细节(例如,将一些值委托给其他对象等)而不会失去向后兼容性。

【讨论】:

嗨乔恩。你能提供一个例子吗。如果我将它设置为公开。 @Deepak:那意味着你失去了对它的很多控制——你不能改变它的名字或它的使用方式,或者线程安全等,因为世界其他地方可以随时阅读。如果没有让其他人在没有验证的情况下更改它等,你不能让它在内部可变。对于最终变量它不是as坏的,但它仍然不是一个好主意IMO . 嗨,乔恩,您能否提供一个失败的代码 sn-pexample。这样会更清楚 @Deepak,首先,一旦您有一些其他代码使用实例变量(实现细节)并且您想要更改它的任何内容(实现),它就会失败。通过拥有访问器和修改器(getter 和 setter),您可以随意更改实现,只要您维护该类型的面向公众的接口的约定。 @Deepak:这不是代码失败的问题 - 这是一个糟糕的设计问题。【参考方案3】:

首先,并非所有实例变量都是私有的。其中一些是受保护的,仍然保留封装。

封装的一般思想是类不应该暴露其内部状态。它应该只使用它来执行它的方法。原因是每个类都有一个所谓的“状态空间”。也就是说,它的字段的一组可能值。它可以控制它的状态空间,但如果它暴露它,其他人可能会将它置于无效状态。

例如,如果你有两个布尔字段,并且类只能在 3 种情况下正常运行:[false, false]、[false, true] 和 [true, false]。如果将字段公开,另一个对象可以设置[true, true],不知道内部约束,并且在原始对象上调用的下一个方法会触发意外结果。

【讨论】:

,你能不能用一个例子来解释一下这句话“但是如果它暴露了它,其他人可能会将它置于无效状态。”【参考方案4】:

将实例变量设为公共或私有是一种设计权衡 设计师在声明类时制作。通过制作实例 变量公开,你公开类实现的细节, 从而提供更高的效率和简洁的表达方式 阻碍未来维护工作的可能费用。经过 隐藏类的内部实现的细节,你有 未来可能改变类的实现 不会破坏任何使用该类的代码。

Oracle White Paper

【讨论】:

【参考方案5】:

正如一些回答者已经指出的那样,实例变量不一定是private,但它们通常至少不设为public,以保持封装性。

我在(我认为)清洁代码中看到了一个示例,它很好地说明了这一点。如果我没记错的话,它是一个复数(如a+bi)类型;无论如何,非常类似的东西,我手边没有这本书。它公开了获取实部和虚部值的方法以及设置实例值的方法。这样做的最大好处是它允许完全替换实现而不破坏代码的任何使用者。例如,复数可以以两种形式之一存储:作为复平面上的坐标 (a+bi),或以极坐标形式 (φ|z|)。将内部存储格式保留为实现细节允许您来回更改,同时仍然在两个表单上显示数字,从而让类的用户选择更方便他们当前执行的操作。

在其他情况下,您可能有一组相关的字段,例如如果字段y 在给定范围内,则字段x 必须具有某些属性。一个简单的例子是x 必须在yy+z 的范围内,对于数值和一些任意值z。通过公开访问器和修改器,您可以在两个值之间强制执行这种关系;如果您直接公开实例变量,则不变量会立即崩溃,因为您不能保证有人不会设置一个而不会设置另一个,或者设置它们以使不变量不再成立。

当然,考虑到反射,仍然可以访问你不应该访问的成员,但是如果有人通过反射你的类来访问私有成员,他们最好意识到他们所做的事情很可能会破坏事情。如果他们使用公共接口,他们可能会认为一切都很好,然后他们最终会遇到令人讨厌的错误,因为他们在不知不觉中没有完全遵守您的特定实现的实现细节。

【讨论】:

【参考方案6】:

在传统的面向对象设计中,一个类将封装数据(变量)和行为(方法)。拥有私有数据将使您在如何实现行为方面具有灵活性,例如,对象可以存储值列表并具有计算并返回这些值的平均值的 getAverage() 方法。稍后,您可以在类中优化和缓存计算的平均值,但合同(即方法)不需要更改。

在过去的几年里,使用anemic data models 变得越来越流行(无论好坏),其中一个类只不过是一堆字段和相应的getter 和setter。我认为在这个设计中你最好使用公共字段,因为 getter 和 setter 没有提供真正的封装,只是让你误以为你在做真正的 OO。

更新:问题链接中给出的示例是这种退化封装的完美示例。我意识到作者试图提供一个简单的示例,但这样做并没有传达封装的任何真正好处(至少在示例代码中没有)。

【讨论】:

【参考方案7】:

    因为如果你改变了类的结构(删除字段等);它会导致错误。但是,如果您有 getX() 方法,您可以在那里计算所需的值(如果字段被删除)。

    你的问题是班级不知道某些内容是否已更改并且无法保证完整性。

【讨论】:

【参考方案8】:

如上所述,保持字段私有具有许多优点。 下一个最佳级别是使用 java 默认访问级别将它们包私有。

默认级别避免您自己的代码混乱,并防止您的代码的客户端设置无效值。

【讨论】:

【参考方案9】:

类用户

我们,正在使用像eclipse,netbins这样的ide...... 看到它建议我们使用公共方法,因此如果类的创建者为私有实例变量提供 getter 和 setter,则您不必记住变量的名称。只需编写 set press ctrl+space 即可获得该类的创建者创建的所有 setter 方法,然后选择所需的方法来设置变量值。

对于类的创建者

有时您需要指定一些逻辑来设置变量值。 “假设你有一个整数变量,它应该存储 0

【讨论】:

以上是关于为啥Java中的实例变量总是私有的?的主要内容,如果未能解决你的问题,请参考以下文章

为啥我的实例变量在我的 Rails 视图中总是 Nil

从子类访问父类的私有实例变量?

为啥Servlet中的实例变量不是线程安全的[重复]

当私有实例变量超出范围时Java终止线程

为啥在 Student(Student s) 中可以使用实例变量? [复制]

关于 Objective-C 中的私有实例变量