Java包私有类的优缺点?


69

我最近正在学习Java,并且遇到了package-private类的概念,如果我们不指定任何内容,这是默认的。但是后来我意识到:

  1. 我很少看到package-private类的使用。是否有这个原因,例如,它有严重的缺点,它是多余的,或者仅仅是我阅读不足?是否有反对使用它的有力论据?

  2. 如果在大多数情况下确实没有用,为什么要使用默认值?

  3. 在实际情况下,我们应该在什么情况下使用package-private?即,什么时候它将变得不可替代?

换句话说,默认的package-private修饰符的主要利弊是什么?

Answers:


58

简短的答案是-它是私有形式的一种。

我假设你熟悉的区别publicprivate,以及为什么它通常是很好的做法,使方法和变量private,如果他们要单独用于内部问题的类。

好吧,作为对此的扩展-如果您正在考虑以模块化方式创建软件,则可以考虑模块的公共接口,该模块内部将具有多个相互协作的类。在这种情况下,public如果要由消费者调用方法,则完全有道理。private如果他们在班级内部;以及package private是否使用它们在该模块中的类之间进行调用,即它是您模块的实现细节(如公共调用者所见),但跨越多个类。

在实践中很少使用它,因为包装系统对于这种事情并不是那么有用。您必须将给定模块的所有类都转储到完全相同的程序包中,对于那些不重要的事情,这将变得有些笨拙。因此,这个主意很棒-使一个方法只能供少数“附近”类访问,private而后者稍宽一些-但对如何定义该类集的限制意味着该方法很少使用/有用。


1
我可以理解为什么有人会这么说-protected修饰符与package private子类相同,只是子类也被邀请参加聚会。但是,如果不自然地创建子类关系(仅出于访问目的),那将是错误的。通常EmailSender,将某个类作为DomainObjectFilter类的子类通常是没有任何意义的,反之亦然。在这种情况下,通常只使用该方法public(也许在注释中说它在概念上不是公开的...)。
Andrzej Doyle

1
“将给定模块的所有类都转储到完全相同的程序包中-笨拙” –正确,但是即使在很简单的情况下,例如在程序包中有20-30个类,我也使用了这种方法。当然,需要进行任何更大的重构。
Victor Sorokin

您不能将源文件组织到目录层次结构中,然后在构建过程中将它们全部转储到同一目录/包中,然后再进行编译吗?那会给您组织结构和一揽子私有访问权的好处,对吗?
jrahhali 2015年

@jrahhali可以,但是我怀疑您会找到愿意兑现的IDE。我认为除非您编写知道您的意思的IDE扩展,否则这会很烦人。
froginvasion

要使用程序包私有可见性修改器,以及将模块与基于层次的修改器一起使用,可以使用实验项目github.com/odrotbohm/moduliths,在这种情况下效果很好,
Lubo

16

关于package-private的一件好事是,您可以使用它来授予对单元测试类本来认为私有的方法的访问权。不利的一面是,程序包中的其他类在真正不应该的时候可以调用它。


5

除了封装之外,使用package-private类的主要优点之一是它们不会出现在项目的javadoc中。因此,如果您使用一些没有其他用途的帮助程序类,而是为了帮助您的公共类执行客户需要的事情,则有必要将它们打包为私有,这是很有意义的,因为您希望使库用户尽可能简单。

例如,您可以看一下我开发的库。所述的Javadoc仅包含5个接口和12个类虽然源代码具有更大量。但是隐藏的大部分是内部层,这些内部层不为客户端提供任何增值(通常所有抽象基类都是隐藏的)。

JDK中也有很多示例。


3

包私有访问级别比protected:更具约束性:仅通过子类化类仍然可以访问受保护的属性和方法。受保护的成员是(或可能是)旨在继承的,而包私有的成员则不是。

通常使用程序包专用成员,因此程序包中的多类可以访问特定实现的属性或(实用程序)方法。

很好的例子是的package-private构造函数StringStringBuilder.valuechar数组:

/*
* Package private constructor which shares value array for speed.
* this constructor is always expected to be called with share==true.
* a separate constructor is needed because we already have a public
* String(char[]) constructor that makes a copy of the given char[].
*/
String(char[] value, boolean share) {
    // assert share : "unshared not supported";
    this.value = value;
}

因此,如果内容已经存在,则java.lang包中的类可以有效地创建新Strings内容,char[]而不会影响安全性。您无法在应用程序中执行此操作,因为如果可以,您将可以访问(引用)String不可变的内部char数组(不计算反射!)。

StringBuilder(或者说AbstractStringBuilder,其中的执行来自)char数组保持的电流值char[] value和存取方法本char[] getValue()也包私人的,从而各种实用方法StringcontentEquals(StringBuffer sb)contentEquals(CharSequence cs)可以利用该对效率和更快的比较而无需内部char数组暴露于世界”。


2

关于“为什么会是默认值”的问题,在这种情况下,术语“默认值”仅表示没有其他限定符。我猜他们可能已经发明了另一个关键字(“ package”已经被采用),但是他们没有。

在现实世界中,我对不希望人们调用或从其他程序包中使用的实用工具类和抽象类使用默认访问权限。假设您有一个接口和两个从某个抽象类扩展的具体实现。您将两个具体的类声明为final,是因为您不一定希望人们将它们子类化(请参阅有效的Java)。同样,您也不希望人们出于同样的原因而无所适从。如果您对抽象类使用默认访问权限,那么只有将他们的类放在您的包中的人才能看到它。它不是防弹的,但我认为这是对默认访问权限的合理使用/说明。就是说,事实并不能阻止细节如私密性那样泄漏,即不能保证任何事情,这意味着

您之所以不经常使用它的另一个原因是,人们倾向于从其javadocs中排除具有默认访问权限的类。


他并没有误解“默认”一词,而是在问为什么它是默认访问级别。设置默认值的原因之一是最常用的东西。还有其他原因,他正在要求他们。
BT

1
我不确定您是对的,但我很乐意为清楚起见更新回应。
jtoberon

1

1-取决于体系结构-通常,如果您只是为自己编写代码,并且在小型项目上编写代码,则可能不会使用它。在较大的项目中,确保您可以控制在何处以及如何调用某些方法会很有帮助。

2-默认值(即非公共/受保护/私有)与私有不同-处于第四状态。请参阅Java访问控制

3-在编写不希望第三方依赖您如何实现基础代码的库时,可以使工作变得更轻松-您只需公开API本身即可。


@BZ默认软件包不是私有的吗?
2011年

1
@BZ其中:如果一个类没有修饰符(默认值,也称为package-private)。感谢您的回答,术语是微不足道的:)
zw324

查看权限表-private和no修饰符之间有区别。包中没有修饰符,任何内容都无法读取-因此不是真正的私有。与私人班级相比,有很大的不同。
BZ。

2
@BZ-术语“默认”和“包私有”通常互换使用。看来您没有注意到Ziyao使用的是复合两个字词,而不是“ private”。
Andrzej Doyle

@BZ是的,我注意到了,这就是为什么我说包私有的原因。猜猜我忘记了连字符:)编辑:好的,添加了连字符。
2011年

-1

“包专用”是指当您有多个包时使用的,这意味着同一包中的其他类可以像“公共”一样访问该类或类成员,其他包中的类则不能像“像它们一样私有”那样访问。


-1

请注意,在谈论类时,只有两种选择:

  1. 公开课
  2. 包私人课程

“私人阶级”的概念毫无意义。(为什么要创建一个在任何地方都不使用的类?!)

因此,如果您有不需要向API用户公开的用于中间操作的类,则应将其声明为“包私有”

同样,当您在同一个源文件中定义多个类时,仅允许一个类为公共类(其名称与.java文件名匹配)。如果在同一文件中定义了其他任何类,则它必须是“包专用”。


3
这个答案充其量是误导的。静态嵌套类是一个示例,其中私有类的概念很有意义。另请参阅:stackoverflow.com/a/34551507
Synoli,
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.