java中的包级保护有什么用?

时间:2008-12-31 17:39:09

标签: java

我知道java中的包级保护是如何工作的。我读取很多代码(包括很多开源代码),似乎没有人使用它。整个保护级别对我来说似乎有点不对(我在一周的任何一天都有c#内部)。

是否存在常用的合法现实用例?

编辑:在问这个问题后有点太晚了,我意识到我忘了排除包受保护的实现类的“标准”模式,可能提供公共接口的实现。每个人都使用这些,正如回复中多次提到的那样。我仍然认为这个问题有很多很好的回复。

15 个答案:

答案 0 :(得分:33)

包级别可见性有两个很好的用途(根据我的经验):

1)在公共API中定义“内部”类。通常,您将接口和核心工厂定义为公共,将“内部”实现定义为包级别。然后,公共工厂可以构造包级实现类,并将它们作为公共接口的实例返回。这很好地允许用户只访问他们应该的东西。

缺点是你必须将所有这些东西放在同一个包中,对于任何合理大小的API来说几乎都不是一个好主意。 Java 7中的JSR 294 / modules / Project Jigsaw有望通过指定一个新的可见性修饰符(module)来提供替代方案,该修饰符可用于跨包访问模块中的类,而不会使它们可见在模块外面。您可以找到一个如何工作的示例in this article

2)单元测试是另一个常见用例。通常你会看到一个src树和一个测试树,否则它们是私有的东西而不是包级别,这样在同一个(并行)包中的单元测试就能够访问其他隐藏的方法来检查或操作状态。

答案 1 :(得分:12)

您是在谈论Java中的包私有保护吗?这是对类成员默认有效的保护。如果你的课程以需要额外信息或方法可见的方式密切互动,那么它偶尔会很有用。

假设您有一个Sink类,并且有几个类可以写入。 Sink有一个接受基本数据类型的公共方法,以及一个接受原始字节数组的包私有方法。你不希望公开这个方法,因为你认为它的用户太低了,但你想让你的其他类(在Sink的包中)写入该Sink使用它。因此,您使方法接受字节数组包私有,并且您的包的类(如ByteStreamSource)可以使用它。现在,您的保护看起来像这样:

     User   Code using the package   User
------ | -----------------------------|----- package public Interface
       |                              |
    Sink <-   package priv. iface  -> Sources

包私有接口与公共方法建立的公共接口正交。包私有性增加了封装,因为它鼓励你公开不应公开的内容。它类似于C ++中的friend关键字和C#中的internal关键字。

答案 2 :(得分:8)

它可以用于实现类,一方面。例如,EnumSet是一个抽象类,文档中没有显示实现类。为什么?因为有两个实现类 - 一个用于具有64个或更少元素的枚举,一个用于65个或更多 - 并且您实际上不需要知道您正在使用哪个。实际上,您甚至不需要知道有多个实现。实际上,您甚至不需要知道EnumSet是抽象的 - 您只需要知道您可以调用其中一个静态方法并获得Set

在这种情况下,私有内部类可能已经足够(虽然不实用,特别是对于大型类)。但有时包中的其他类需要访问此类实现细节。 protected可以工作,但是他们也会对子类开放。

简而言之,它涵盖了其他三个保护级别无法处理的封装区域。

答案 3 :(得分:5)

我认为原则上包级保护很有意义。它允许您创建靠近模块(包)的东西,但只暴露核心类。

例如,我的许多项目都使用公共接口和实现它们的“隐藏”类。用户使用工厂,并接收对应于“隐藏”具体实现的接口的引用。有了封装保护,理论上我应该只能暴露接口和工厂。

不幸的是,因为包保护不适用于子包,所以它并不适合我的工作方式。我喜欢使用其他包,特别是我的“内部”东西的子包(与Eclipse的组织方式相同)。因此,我的包无法访问子包中的包级保护的东西。我希望有一天能改变这种状况。

答案 4 :(得分:3)

默认的protexction,即“包”保护意味着它对包是私有的,但可以“看到”包中的类不是私有的。一个用途是用于包的辅助类;假设您有一个管理您不希望显示的池资源的类,您可以将它放在默认保护类中。现在它可以被包中的所有东西使用,它可以访问其他类的任何受保护的内部,但它不是用户可见包的一部分。

答案 5 :(得分:3)

Java中默认保护的另一个用途是提高性能。

允许内部类访问其包含类的私有成员,但这是通过混淆的访问器方法实现的。在JIT编译器优化它之前(或者在没有JIT的环境中,例如J2ME),这会导致性能损失很小。使用默认保护声明所需的方法消除了对这些访问器方法调用的需要,但不需要使成员完全公开。

请记住,只有在知道性能至关重要的情况下才会这样做,并且您不能指望JVM为您解决问题。但至少它并没有像其他许多微优化那样损害可读性/可维护性。

答案 6 :(得分:2)

在我看来,方法和字段的包私有(默认)访问是无用的。事实上,它比无用更糟糕,它是有害的,因为它通常用于提供访问某些应该是私人的成员,但不是为了方便。

但是,包私有类 非常有用。您可能希望使用package-private类提供接口的实现。可以声明工厂方法以返回实现给定接口的对象。在这种情况下,知道哪个类为接口提供实现并不重要。通过使类包私有,它不是公共API的一部分,因此可以在将来的版本中进行修改或替换。

在Oak中,后来成为Java的语言there were only 3 access levels

如果我现在正在重新设计Java,我将摆脱当前的默认值(package-private)并将private设为默认值。私有类将是其封闭范围(包)中的私有类,它将完全依赖于包 - 私有类如何工作与成员使用'private'更加一致。

答案 7 :(得分:2)

没有人使用包级别访问的原因可能是一种心理影响。每个Java介绍都会传播封装,这被理解为将所有字段声明为私有。私人访问通常过于狭窄,因此必须添加公共getter / setter。然后在整个代码库中重复这种模式:私有字段和公共方法无处不在。具有讽刺意味的是,该模式颠覆了封装,因为整个实现基本上被getter / setter对钉死了。

一个类主要是内存分配的单元,在很多情况下,它太小而无法为程序的其余部分提供有意义的接口。使用包作为封装单元是一个更好的主意,并专注于为它们提供定义良好的公共接口。

1.7中的超级包可能会改变游戏规则。

答案 8 :(得分:1)

我有需要彼此访问的对象。在包中,他们可以调用这些受保护的方法,但是当有人试图访问这些受保护的方法时,它们是不允许的。

在我看来,这是一种基本的保护机制,保护层次将是一个很好的功能。

答案 9 :(得分:1)

如今,软件包通常用于模拟“软件组件”,即软件包是一组以某种方式相关的类。由于“public”方法定义了软件组件的外部接口,而“private”方法/成员是类的实现细节,因此默认的“package”可见性相当于组件内部方法。

类比可能是C ++中的“朋友”方法。

答案 10 :(得分:1)

我认识5到10年以上的Java老手,他们没有意识到protected意味着包私人访问。对我而言,这一点使得包私有一种可怕的语言功能。就个人而言,我认为无论如何都没有任何正当理由使用私人套餐。让我们考虑用例:

  • 打包私有类:使用内部类。另一张海报表明,对于内部类和包私有类,存在(小)性能损失,但对我来说,这不是设计糟糕的好理由。打包私有和内部类我认为是实现细节,因此,我认为最好将它们“隐藏”为内部类;
  • 打包私有数据成员:我完全没有理由看到这些成员;和
  • 打包私有方法:这些方法几乎相当于C ++好友方法。 C ++朋友有一个很好的存在理由,那就是在类之外外部化运算符重载,这允许第一个参数不是类本身。在Java中,没有这样的用例,只留下封装和抽象的最终运行。

将此与受保护的方法进行比较,这在设计扩展类时是完全合理的。我见过程序员无意中在同一个程序包中的不相关类中使用受保护方法的情况,因为它们出现在自动完成列表中。

绝对没有办法阻止这种情况。

C#有一个更好的访问系统,因为受保护并不意味着内部访问。但我认为这一点 - 以及一个可变的Date类 - 是一个非常大的缺陷。

答案 11 :(得分:1)

主要用于单元测试,其中源和测试文件位于同一个包中,测试可以访问类的内部而不会暴露它。

答案 12 :(得分:1)

我现在正在讨论这些问题中的许多问题,这是一个很好的讨论。我一直选择按照应有的方式将所有私有事物私有化,但后来我正在编写大量反映单元测试私有范围的代码。这是,我认为纯粹的OO方式来做到这一点。但是,我发现可以密封jar文件,以便将包范围限制在模块(jar)内部。在我看来,这使包装范围更加可口。我可能愿意打破良好的封装,以换取将我的单元测试代码减少到原来的一小部分。我们的想法是,如果有人在同一个库中工作,并且在相同的包中,他们将与代码库保持足够的密切关系,以便在没有合理的设计辩论的情况下不从类外访问包范围成员。这仍然是一个艰难的决定。为这种类型的实例设置一个朋友类类型的指定会很好,这样我的单元测试代码代码就可以访问我的私有代码,但不能被其他类访问。我刚刚通过maven密封了我的jar文件:

<build>
    <plugins>
        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-jar-plugin</artifactId>
            <version>2.3.2</version>
            <configuration>
                <archive>
                    <index>true</index>
                    <manifest>
                        <addClasspath>true</addClasspath>
                    </manifest>
                    <manifestEntries>
                        <sealed>true</sealed>
                    </manifestEntries>
                </archive>
            </configuration>
        </plugin>

现在我在讨论在我的单元测试中拔出一堆反射代码。

我仍处于辩论的边缘。包范围非常开放,特别是如果你有许多开发人员在代码库中蹦蹦跳跳。

答案 13 :(得分:0)

通常,默认/包保护可用于使“公共”类和变量更受限制。

你只需要从你的班级中导出一些方法(如果你是一名优秀的程序员),公开标记它们,同时保持尽可能私密。

包保护允许您只从包中导出一些类,其余类保密。

从理论上讲,这是一个好主意,在实践中你几乎从未见过它。

答案 14 :(得分:0)

“我知道java中的包级别保护如何工作......并且似乎没有人使用它。”

他们在使用什么?

他们是否公开所有课程?

负担原则有两种形式。

强形式表明,转换实体集合的负担是转换实体数量的函数。弱形式表明,转化实体集合的最大潜在负担是转化实体的最大潜在数量的函数。

国际标准化组织将封装定义为只能通过对象支持的接口上的交互来访问对象中包含的信息的属性。

在更高的抽象层次上,程序单元也可以封装在子系统中,子系统中包含的信息只能通过子系统中包含的公共程序单元访问。

创建或修改任何软件系统的负担是创建或修改的程序单元数量的函数。

依赖于特定的,经过修改的程序单元的程序单元比不依赖于修改的程序单元的程序单元具有更高的受影响概率。

修改后的计划单位可能施加的最大潜在负担是所有依赖它的计划单位的影响。

减少对已修改程序单元的依赖性因此降低了其更新将影响其他程序单元的可能性,从而减少该程序单元可能施加的最大潜在负担。

减少系统中所有程序单元之间最大可能的依赖关系数因此降低了对特定程序单元的影响将导致更新其他程序单元的可能性,从而减少所有更新的最大潜在负担。

封装理论展示了如何使用封装来减少所有程序单元之间最大可能的依赖数。

封装理论因此展示了如何使用封装来减轻负担原则的弱形式。

在Java中,使类包成为私有是减少系统中最大可能的依赖数量的关键机制之一,从而减少对该系统进行任何软件修改的最大可能负担。

但是你提到在你阅读的代码中没有使用它。

听起来很奇怪。