Java面向对象编程中的Public、Private和Protected区别

7
我了解public、private和protected的作用。我知道你应该使用它们来遵循面向对象编程的概念,并且知道如何在程序中使用多个类来实现它们。
我的问题是:为什么我们要这样做?为什么不能直接让一个类修改另一个类的全局变量?即使您不应该这样做,为什么还需要protected、private和public修饰符?好像程序员不相信自己不会这样做,尽管他们是编写程序的人。
提前感谢。

1
这不是关于信任的问题,而是关于创建一个明确定义的接口,该接口与其内部实现分离。 - Oliver Charlesworth
6个回答

9

你说得对,这是因为我们不能相信自己。可变状态是计算机程序复杂性的主要因素,很容易构建一些一开始看起来还不错的东西,但随着系统变得越来越大,它就会失控。限制访问有助于减少对象状态以不可预测的方式改变的机会。这个想法是让对象通过定义良好的通道进行通信,而不是直接调整彼此的数据。这样我们就有希望测试单个对象,并对它们作为整个系统的一部分的行为有一些信心。


听起来不错。这与真实世界的企业级Java开发有什么关系?如果有,我的同事们会在意吗? - ispilledthejava
@nathan:你能解释一下问题吗?我怎么可能知道关于你的同事的任何信息呢? - Nathan Hughes

7

让我举个简单的例子(仅供示范):

class Foo {
  void processBar() {
    Bar bar = new Bar();
    bar.value = 10;
    bar.process();
  }
}

class Bar {
  public int value;
  public void process() {
    // Say some code 
    int compute = 10/value;
    // Her you have to write some code to handle
    // exception
  }
}

一切看起来都很好,你感到高兴。但后来你意识到其他开发人员或者你使用的其他API将值设置为0,导致在Bar.process()函数中出现异常。

现在根据上述实现,你无法阻止用户将其设置为0。现在看下面的实现。

class Foo {
  void processBar() {
    Bar bar = new Bar();
    bar.setValue(0);
    bar.process();
  }
}

class Bar {
  public int value;
  public void setValue(int value) {
    if(value == 0)
      throw new IllegalArgumentException("value = 0 is not allowed");

    this.value = value; 
  }
  public void process() {
    // Say some code
    int compute = 10/value;
    // No need to write exception handling code
    // so in theory can give u better performance too

  }
}

现在不仅可以进行检查,还可以提供有用的异常信息,帮助快速发现错误并在早期解决。

这只是一个例子,面向对象编程的基础(封装、抽象等)可以帮助您标准化接口并隐藏底层实现。


4
请记住,编写给定类的开发人员可能不是唯一使用它的人。 开发团队编写软件库,在Java中通常作为JAR分发,由完全不同的开发团队使用。 如果没有这些标准,其他人至少很难知道任何可用变量/方法的意图。

例如,如果我有一个私有/受保护的实例变量,我可能有一个公共的“setter”方法来检查有效性、前提条件和执行其他活动——如果任何人都可以自由地直接修改实例变量,所有这些都将被绕过。

Java文档/教程中的另一个好处在于:http://docs.oracle.com/javase/tutorial/java/javaOO/accesscontrol.html

公共字段倾向于将您链接到特定的实现,并限制您更改代码的灵活性。


2
  • 非本地行为很难理解。
  • 最小化表面积可以增加理解。
  • 我不相信自己能记住所有行为副作用。
  • 我绝对不相信“你”能理解所有行为副作用。
  • 暴露的越少,我就越有灵活性去修改和扩展。

2
我的问题是:为什么要这样做?
基本上,因为通过这种方式的限制,我们使得自己和其他可能需要在将来阅读/修改代码的人更容易理解代码以及各个部分之间的相互作用。
大多数开发人员通过抽象的心理过程理解代码;即精神上围绕着代码块画出边界,了解每个代码块本身,然后了解每个代码块如何与其他代码块交互。如果任何代码的一部分可能潜在地干扰到其他代码的“内部”,那么典型的开发人员就很难理解正在发生什么。
在编写代码时,这可能不是您的问题,因为您可以在创建代码时记住所有复杂的交互。但是在一两年的时间里,您会忘记很多细节。而其他人从一开始就没有这些细节。
为什么不能让一个类直接修改另一个类的全局变量?
因为这会使您的代码更难理解;见上文。您的代码库越大,问题就会越明显。
另一点是,如果您过度使用全局变量(实际上是静态变量),则会在代码需要多线程/可重入性、单元测试以及需要在其他上下文中重用代码时创建问题。
即使您不应该这样做,为什么还需要受保护、私有和公共修饰符?
这不是关于信任的问题。它是关于在源代码中表达边界的问题。
如果我编写一个类并声明一个方法或字段为private,则我知道我不必考虑其他类调用/访问/修改它时会发生什么问题。如果我正在阅读别人的代码,则当映射交互和边界时,我可以(最初)忽略私有部分。private、protected修饰符和包私有只提供不同粒度的边界。
(或者也许这是关于信任的问题;即不信任自己记得我们设计中的抽象边界在哪里/在哪里)。

1

基本上有两个原因:

1)在Java中需要安全接口。例如,在您的计算机上运行Java小程序时,您希望确保该小程序无法访问未经授权的文件系统部分。如果没有可执行的安全性,小程序可以进入Java安全层并修改自己的权限。

2)即使每个人都是“可信任的”,有时候追求效率胜过常识,程序员会绕过API来访问内部接口,而不是增强API(尽管这通常比实际操作时间更长)。这会为稳定性和升级兼容性带来问题。

(在计算机古老的历史中,有一个传说,一种操作系统被应用程序员以这种方式处理,以至于维护操作系统的程序员被迫确保某些代码段(不是入口点,而是实际的内部代码序列)在操作系统修订时不改变物理地址。)

请注意,这些问题存在于面向对象编程范式变得普遍之前,并且是面向对象编程的动机之一。面向对象编程不是凭空发明的任意宗教教义,而是通过大约60年的编程经验过滤出的一组原则。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接