题 在Java中,包private,public,protected和private之间的区别


在Java中,是否有明确规定何时使用每个访问修饰符,即默认值(包私有), publicprotected 和 private制作时 class 和 interface 处理继承?


2500
2017-10-18 19:53


起源


private 隐藏在包中的其他类。 public 暴露给包外的类。 protected 是一个版本 public 仅限于子类。 - Museful
@Tennenrishin - 不;与C ++相反,在Java中 protected 使该方法也可以从整个包中访问。 Java的可见性模型中的这种愚蠢打破了目标 protected。 - Nicolas Barbulesco
@Nicolas无论有没有,都可以从整个包装中获取 protected。作为访问 修改那一切 protected 是暴露给包外的子类。 - Museful
@tennenrishin - 嗯,这就是尼古拉斯所说的......而你现在只是在重复它。你最初说的是那个 protected  - 我引用 - '是一个仅限于子类的公共版本',这是你自己承认的不正确,因为受保护也允许通过整个包访问(ergo,它不 限制 访问子类。) - luis.espinal
我也同意Nicolas的观点,即Java中受保护的访问模式是愚蠢的。发生的事情是Java混合了水平(点阵)和垂直访问限制限定符。默认范围是水平/格子限制,格子是包。公共是另一个横向限制,格子是整个世界。私有和(C ++)保护是垂直的。如果我们有一个交叉访问权限会更好,比方说, protected-package 对于我们实际需要它的罕见情况,离开 protected 相当于受保护的C ++版本。 - luis.espinal


答案:


官方教程 可能对你有用。

            │班级│包装│子类│子类│世界
            │││(相同的pkg)│(diff pkg)│
────────────┼───────┼─────────┼──────────┼──────── ──┼────────
公开│+│+│+│+│+
────────────┼───────┼─────────┼──────────┼──────── ──┼────────
受保护的│+│+│+│+│
────────────┼───────┼─────────┼──────────┼──────── ──┼────────
没有修饰符│+│+│+││
────────────┼───────┼─────────┼──────────┼──────── ──┼────────
私人│+││││

+:可访问
空白:无法访问

4660
2017-10-18 19:57



如果你试图访问同一个类中的受保护方法或实例变量,但是在一个不是你的对象上,就像.equals(Klass var)一样,它会起作用吗? - Jordan Medlock
如果子类与其父类位于同一个包中,则默认字段在子类中可见。 - Maksim Dmitriev
在Java版本和高达1.0.1的JDK中,您可以使用private和protected一起创建另一种形式的保护,它将仅限于对给定类的子类访问方法或变量。 - Vitalii Fedorenko
对于一个没有回答问题的答案,这是很多选票 什么时候 我们应该使用每个可见性(而不是效果)。 - Bohemian♦
@Bohemian - 答案假设您应该尽可能使用最不宽容的访问说明符(或者假设您通过遵循提供该建议的链接阅读完整答案)。 - iheanyi


(警告:我不是Java程序员,我是Perl程序员.Perl没有正式的保护,这也许是我理解这个问题的原因:))

私人的

就像你想的那样,只有  声明它可以看到它。

包私人

只能被看到和使用  在其中宣布。这是Java中的默认值(有些人认为是错误的)。

受保护

Package Private +可以通过子类或包成员看到。

上市

每个人都可以看到它。

发布时间

在我控制的代码外面可见。 (虽然不是Java语法,但对于此讨论非常重要)。

C ++定义了一个名为“friend”的附加级别,你知道的越少越好。

你什么时候应该用什么?整个想法是隐藏信息的封装。您希望尽可能隐藏用户完成某些操作的详细信息。为什么?因为那样你可以在以后更改它们而不会破坏任何人的代码。这使您可以优化,重构,重新设计和修复错误,而无需担心有人正在使用您刚刚彻底检查过的代码。

因此,经验法则是使事物只能像它们必须一样可见。从私有开始,只根据需要添加更多可见性。只公开那些对用户来说绝对必要的信息,你公开的每一个细节都会让你重新设计系统。

如果您希望用户能够自定义行为,而不是将内部公开,以便他们可以覆盖它们,那么将这些内容推入对象并使该接口公开通常是一个更好的主意。这样他们就可以简单地插入一个新对象。例如,如果您正在编写CD播放器并希望“查找有关此CD的信息”位可自定义,而不是将这些方法公开,您可以将所有功能放入其自己的对象中并使公共getter / setter成为公共对象。以这种方式吝啬暴露你的胆量鼓励良好的成分和关注点的分离

就个人而言,我坚持只是“私人”和“公共”。许多OO语言就是这样。 “受保护”可以派上用场,但这真的是一种骗局。一旦界面不仅仅是私人的,它就在你的控制之外,你必须去寻找其他人的代码才能找到用途。

这就是“已发布”的概念所在。更改界面(重构它)需要您找到使用它的所有代码并进行更改。如果界面是私有的,那么没问题。如果它受到保护,你必须找到你所有的子类。如果它是公开的,你必须找到使用你的代码的所有代码。有时这是可能的,例如,如果您正在处理仅供内部使用的公司代码,那么如果接口是公共的则无关紧要。您可以从公司存储库中获取所有代码。但是如果一个界面被“发布”,如果有代码在你的控制范围之外使用它,那么你就被软管了。您必须支持该接口或冒险破坏代码。甚至受保护的接口也可以被认为是已发布的(这就是我不打扰受保护的原因)。

许多语言发现公共/受保护/私有的等级性质太过限制而不符合现实。为此,有一个概念 特质班,但这是另一个节目。


358
2017-10-18 21:17



朋友 - >“你知道的越少越好”--->它提供了选择性的可视性,这仍然优于包隐私。在C ++中,它有其用途,因为并非所有函数都可以是成员函数,而且朋友比公共更好。当然存在被邪恶思想滥用的危险。 - Sebastian Mach
还应该注意,C ++中的“protected”具有不同的含义 - 受保护的方法实际上是私有的,但仍然可以从继承类中调用。 (与Java相反,它可以被同一个包中的任何类调用。) - Rhys van der Waerden
@RhysvanderWaerden C#在这方面与C ++相同。我发现很奇怪Java不允许声明子类可访问的成员而不是整个包。这对我来说有点颠倒 - 包装的范围比儿童类更宽! - Konrad Morawski
@KonradMorawski IMHO包的范围小于子类。如果您尚未将类声明为final,则用户应该能够将其子类化 - 因此java protected是已发布接口的一部分。 OTOH,包由单个组织隐式开发:例如com.mycompany.mypackage。如果您的代码在我的包中声明了自己,那么您隐含地声明自己是我的组织的一部分,因此我们应该进行沟通。因此,包发布给更小/更容易接触的受众(我公司的人)而不是子类(扩展我的对象的人),因此被视为较低的可见性。 - Eponymous
friend 适用于定义类之间的特殊关系。在正确使用的情况下,它在许多情况下允许优越的封装例如,特权工厂类可以使用它将内部依赖项注入构造类型。它的名字很糟糕,因为不关心正确维护设计良好的对象模型的人可以滥用它来减轻他们的工作量。 - Dennis


这是表格的更好版本。 (模块专栏的未来证明。)

Java Access Modifiers

说明

  • 一个 私人的 成员是 只要 可以在声明的同一类中访问。

  • 有会员 没有访问修饰符 只能在同一个包中的类中访问。

  • 一个 保护 成员可以在同一个包中的所有类中访问  在其他包的子类中。

  • 一个 上市 所有类都可以访问成员(除非它位于  不导出它声明的包)。


选择哪种修饰符?

访问修饰符是一种帮助您防止意外破坏封装的工具(*)。问问自己,您是否希望该成员成为类,包,类层次结构内部的内容,或者根本不是内部成员,并相应地选择访问级别。

例子:

  • 一个字段 long internalCounter 应该是私有的,因为它是可变的和实现细节。
  • 只应在工厂类(在同一个包中)实例化的类应具有包受限制的构造函数,因为不应该直接从包外部调用它。
  • 一个内部 void beforeRender() 在渲染之前调用的方法以及在子类中用作钩子的方法应该受到保护。
  • 一个 void saveGame(File dst) 从GUI代码调用的方法应该是公共的。

(*) 什么是封装?


245
2017-11-10 10:27





                | highest precedence <---------> lowest precedence
*———————————————+———————————————+———————————+———————————————+———————
 \ xCanBeSeenBy | this          | any class | this subclass | any
  \__________   | class         | in same   | in another    | class
             \  | nonsubbed     | package   | package       |    
Modifier of x \ |               |           |               |       
————————————————*———————————————+———————————+———————————————+———————
public          |              |          |              |      
————————————————+———————————————+———————————+———————————————+———————
protected       |              |          |              |   ✘   
————————————————+———————————————+———————————+———————————————+———————
package-private |               |           |               |
(no modifier)   |              |          |       ✘       |   ✘   
————————————————+———————————————+———————————+———————————————+———————
private         |              |     ✘     |       ✘       |   ✘    

165
2018-01-09 21:42



值得一提的是 - “Protected modifier使对象在其他包中可用,而default / no-modifier限制对同一包的访问” - vanguard69
@ vanguard69 ,. protected 修饰符使标记 事情 (类,方法或字段)可用于某些其他包中的其他类 只有iff 说其他类是该类的子类 protected - 标记 事情 宣布。 - Abdull
“nonsubbed”? “这个子类在另一个包中”?呵呵。我以为我认识Java。 - sehe
@AlexanderFarber你是否针对特定的浏览器配置进行了优化? 这是我的铬现在 这是 火狐 - sehe
嗯,让我们恢复我的改变 - Alexander Farber


简单的规则。首先声明一切都是私密的。然后随着需求的出现向公众发展,设计需要保证。

当暴露成员时,问问自己是否暴露了表示选择或抽象选择。第一个是你想要避免的东西,因为它会在实际表示中引入太多依赖,而不是它的可观察行为。

作为一般规则,我尝试通过子类化来避免重写方法实现;搞砸逻辑太容易了。如果要覆盖它,则声明抽象受保护的方法。

此外,在重写时使用@Override注释可以防止重构时出现问题。


131
2017-10-18 20:00



@RuchirBaronia,“world”=应用程序中的所有代码,无论它在哪里。 - Andrejs


它实际上比简单的网格显示要复杂一些。网格告诉您是否允许访问,但是访问的确切构成是什么?此外,访问级别以复杂的方式与嵌套类和继承交互。

还调用“默认”访问(由缺少关键字指定) 包私有。例外:在界面中,没有修饰符意味着公共访问;禁止公开以外的修饰语。枚举常量总是公开的。

概要

是否允许访问具有此访问说明符的成员?

  • 会员是 private:仅当成员与调用代码在同一个类中定义时。
  • 成员是包私有:仅当调用代码在成员的立即封装包内时。
  • 会员是 protected:相同的包,或者如果成员是在包含调用代码的类的超类中定义的。
  • 会员是 public:是的。

什么访问说明符适用于

局部变量和形式参数不能使用访问说明符。由于根据范围规则它们本身就无法进入外部,因此它们实际上是私密的。

仅适用于顶级作用域中的类 public 允许包和私有。这种设计选择大概是因为 protected 和 private 在包级别是多余的(没有包的继承)。

所有访问说明符都可以在类成员(构造函数,方法和静态成员函数,嵌套类)上使用。

有关: Java类可访问性

订购

访问说明符可以严格排序

public> protected> package-private> private

意思是 public 提供最多的访问, private 至少。私有成员可能的任何引用也对包私有成员有效;对包私有成员的任何引用在受保护的成员上都是有效的,依此类推。 (允许访问受保护的成员到同一个包中的其他类被认为是一个错误。)

笔记

  • 一类的方法  允许访问同一类的其他对象的私有成员。 更确切地说,类C的方法可以访问C的任何子类的对象上的C的私有成员.Java不支持通过实例限制访问,仅限于类。 (与Scala相比,后者支持使用它 private[this]。)
  • 您需要访问构造函数才能构造对象。因此,如果所有构造函数都是私有的,则该类只能由生活在类中的代码构造(通常是静态工厂方法或静态变量初始化器)。类似地,对于包私有或受保护的构造函数。
    • 只有私有构造函数也意味着该类不能在外部进行子类化,因为Java需要子类的构造函数来隐式或显式地调用超类构造函数。 (但是,它可以包含一个嵌套类,它将它子类化。)

内课

你还必须考虑 嵌套 范围,例如内部类。复杂性的一个例子是内部类具有成员,它们本身可以使用访问修饰符。所以你可以拥有一个公共成员的私人内部阶级;会员可以访问吗? (见下文。)一般规则是查看范围并递归思考,看看是否可以访问每个级别。

但是,这非常复杂,而且详情如下, 参考Java语言规范。 (是的,过去有过编译器错误。)

要了解这些如何相互作用,请考虑此示例。有可能“泄漏”私人内部阶级;这通常是一个警告:

class Test {
    public static void main(final String ... args) {
        System.out.println(Example.leakPrivateClass()); // OK
        Example.leakPrivateClass().secretMethod(); // error
    }
}

class Example {
    private static class NestedClass {
        public void secretMethod() {
            System.out.println("Hello");
        }
    }
    public static NestedClass leakPrivateClass() {
        return new NestedClass();
    }
}

编译器输出:

Test.java:4: secretMethod() in Example.NestedClass is defined in an inaccessible class or interface
        Example.leakPrivateClass().secretMethod(); // error
                                  ^
1 error

一些相关问题:


94
2017-09-13 07:38





根据经验:

  • 私人的:类范围。
  • 默认 (要么 包私有):包范围。
  • 保护:包范围 +孩子 (比如包,但我们可以从不同的包中继承它)。 protected修饰符始终保持“父子关系”。
  • 上市:无处不在。

因此,如果我们将访问权限分为三个权限:

  • (直接 (从同一个类中的方法调用)。
  • (参考 (使用对类的引用或通过“点”语法调用方法)。
  • (遗产 (通过子类化)。

然后我们有这个简单的表:

+—-———————————————+————————————+———————————+
|                 |    Same    | Different |
|                 |   Package  | Packages  |
+—————————————————+————————————+———————————+
| private         |   D        |           |
+—————————————————+————————————+———————————+
| package-private |            |           |
| (no modifier)   |   D R I    |           |
+—————————————————+————————————+———————————+
| protected       |   D R I    |       I   |
+—————————————————+————————————+———————————+
| public          |   D R I    |    R  I   |
+—————————————————+————————————+———————————+

63
2017-12-18 18:01





很短的

  • public:无处不在。
  • protected:可以通过相同包的类和驻留在任何包中的子类访问。
  • default(未指定修饰符):可由同一包的类访问。
  • private:只能在同一个类中访问。

43
2017-10-27 17:49