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

IT小君   2021-10-25T02:52:41

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

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

我有两个疑问:

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

如果实例变量在 Java 类中被声明为 public,你能用一个例子来解释什么会出错吗?

点击广告,支持我们为你提供更好的服务
评论(9)
IT小君

实例变量是私有的,以强制那些类的用户使用方法来访问它们。在大多数情况下,有普通的 getter 和 setter,但也可以使用其他方法。

例如,使用方法将允许您将访问限制为只读,即如果没有 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");

因此,您不能在不破坏其他代码的情况下添加检查(它不再编译)。

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

2021-10-25T02:52:41   回复
IT小君

他们不具备成为私人-但他们应该。一个字段是一个实现细节——所以你应该把它保密。如果你想允许用户获取或设置它的值,你可以使用属性来这样做(get 和 set 方法)——这可以让你安全地完成(例如验证输入),还允许你更改实现细节(例如委托其他对象的某些值等)而不会失去向后兼容性。

2021-10-25T02:52:41   回复
IT小君

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

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

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

2021-10-25T02:52:42   回复
IT小君

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

甲骨文白皮书

2021-10-25T02:52:42   回复
IT小君

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

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

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

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

2021-10-25T02:52:42   回复
IT小君

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

过去几年(无论好坏)使用贫血数据模型变得越来越流行 ,其中一个类只不过是一堆字段和相应的 getter 和 setter。我会争辩说,在这种设计中,您最好使用公共字段,因为 getter 和 setter 没有提供真正的封装,而只是让您误以为您在做真正的 OO。

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

2021-10-25T02:52:42   回复
IT小君
  1. 因为如果你改变了类的结构(删除字段等);它会导致错误。但是如果你有一个getX()方法,你可以在那里计算所需的值(如果字段被删除)。

  2. 那么您遇到的问题是,该类不知道某些内容是否已更改并且无法保证完整性。

2021-10-25T02:52:43   回复
IT小君

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

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

2021-10-25T02:52:43   回复
IT小君

对于班级用户

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

对于类的创建者

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

2021-10-25T02:52:43   回复