Java 中包私有类的优缺点?

我最近在学 Java,我偶然发现了 package-private类的概念,如果我们不指定任何东西,这是默认的。但后来我意识到:

  1. 我很少看到包-私有类的使用。这是否有原因,例如,它有严重的缺点,它是多余的,或者仅仅是我读得不够多?是否有强有力的论据支持或反对它的使用?

  2. 如果它在大多数情况下真的没有用,为什么会是默认的呢?

  3. 在什么情况下我们应该使用包-私有在现实世界中? 也就是说,什么时候它会成为不可替代的?

换句话说,默认包-私有修饰符的主要优点和缺点是什么?

42793 次浏览

取决于架构——一般来说,如果你只为自己编写代码,在小型项目中你可能不会使用它。在大型项目中,确保您可以控制在何处以及如何调用某些方法是很有帮助的。

2-默认值(即不是 public/protected/private)与 private 不同-它是第4个状态。参见 Java 访问控制

3-当你编写不希望第三方依赖于你如何实现底层代码的库时,它可以让你的工作变得更简单——你只需要让 API 本身公开。

简短的回答是-这是一个稍微宽泛的私人形式。

我假设您已经熟悉了 publicprivate之间的区别,以及如果方法和变量仅在内部用于所讨论的类,那么使用 private通常是很好的做法的原因。

作为扩展,如果你想用模块化的方式创建你的软件,你可以考虑一个到你的 模组的公共接口,里面有多个类在它们之间协作。在这种情况下,如果方法要被使用者调用,那么使用 public是非常有意义的; 如果方法是类的内部方法,那么使用 private; 如果方法用于在模块中的类之间调用,那么使用 package private是非常有意义的,也就是说,它是模块的一个实现细节(公共调用者可以看到) ,但是跨越几个类。

这在实践中很少使用,因为包系统对于这类事情并不是很有用。您必须将给定模块的所有类转储到完全相同的包中,对于任何非平凡的东西,这都会变得有点笨拙。所以这个想法很棒——让一个方法只能被少数几个“附近”的类访问,作为一个稍微宽一点的 private——但是对于如何定义这组类的限制意味着它很少被使用/有用。

关于“为什么它会是默认的”这个问题,在这个上下文中,术语“默认”仅仅意味着缺少另一个限定词。我猜他们本可以发明另一个关键词(“包裹”已经被拿走了) ,但他们没有。

在现实世界中,我对实用程序类和抽象类使用默认访问权限,我不希望人们调用这些类或从其他包中使用这些类。假设您有一个接口和两个从某个抽象类扩展的具体实现。您将两个具体的类声明为 final 类,因为您不一定希望人们对它们进行子类化(请参见有效的 Java)。出于同样的原因,您也不希望人们在您的抽象类上胡闹。如果对抽象类使用默认访问,那么人们只有在将类放入包中时才会看到它。它不是万无一失的,但我认为它是默认访问的合理使用/说明。也就是说,它不能像保密那样防止细节泄露,也就是说不能保证任何事情,这意味着它不是一个特别有用的约定。

另一个原因是人们倾向于从 javadocs 中排除具有默认访问权限的类。

“ Package Private”用在你有几个包的时候,这意味着,同一个包中的其他类可以以“ public”的形式访问该类或类成员,其他包中的类不能访问,就像“ Private like them”一样

Package-private 的一个好处是,您可以使用它来访问您原本认为是私有的单元测试类的方法。当然,缺点是包中的其他类可以在实际不应该调用它的时候调用它。

请注意,当你谈论课程时,你只有两个选择:

  1. 公开课
  2. 包私有类

“私有类”的概念是没有意义的。(为什么要创建一个不在任何地方使用的类? !)

因此,如果您有一个用于中间操作的类,不需要向 API 用户公开,那么您应该将其声明为“ package private”

此外,当在同一源文件中定义多个类时,只允许一个类为公共类(其名称与。Java 文件名)。如果在同一个文件中定义了任何其他类,那么它必须是“ package private”。

除了封装之外,使用包私有类的一个主要优点是它们不会出现在项目的 javadoc 中。因此,如果您使用一些辅助类,这些辅助类除了帮助您的公共类完成客户机需要的任务外没有其他用途,那么将它们设置为私有包是有意义的,因为您希望为库的用户保持尽可能简单的操作。

例如,您可以查看我开发的一个库。Javadoc只包含5个接口和12个类,尽管 源代码有更多的接口和类。但是隐藏的主要是内部层,它们不为客户机提供任何附加值(通常所有的抽象基类都是隐藏的)。

JDK 中也有许多示例。

包私有访问级别比 protected限制更多: 受保护的属性和方法仍然可以通过简单的子类来访问。受到保护成员是(或可能是)用于继承的,而包私有成员则不是。

包私有成员通常用于使包中的多个类可以访问 特定实施方案属性或(实用程序)方法。

这方面的好例子是 StringStringBuilder.value字符数组的 package-private 构造函数:

/*
* 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;
}

因此,如果内容已经存在于 char[]中,那么 java.lang包中的类可以有效地创建新的 Strings,而不会损害安全性。您不能在应用程序中这样做,因为如果可以的话,您将可以访问(引用) String的内部字符数组,该字符数组是不可变的(不计算反射).

StringBuilder(或者更确切地说,实现来自 AbstractStringBuilder)中,保存当前值 char[] value的字符数组和这个 char[] getValue()的访问器方法也是包-私有的,因此 String的各种实用方法,如 contentEquals(StringBuffer sb)contentEquals(CharSequence cs),可以利用这种方法进行高效率和更快速的比较,而不会将内部字符数组暴露给“世界”。