前言
曾经学 Java的时候看过一本中文教材,其中讲到 Java的可访问性(Accessibility)一章内,提到了4个关键词(Keywords),这四个分别是public, protected, friendly(友好的), private
,其中不用说的就是其访问权限是由高到低。但是这天有些许个问题,于是想在官方文档里找个答案,不曾想回忆起了这些个以前的问题———国内某些中文教材里所谓的friendly
真的是存在吗?
正文
经过查阅,在 Java Language Specification (Java SE 13 Edition) 里压根就没有 friendly
这么一个说法, §3.9 Keywords
这章节内没有, §6.6 Access Control
内也没有,那么这个 friendly
从何而来,我认为去追究一个也许是错误的东西的起源没有太大的意义,纠正这个认知反而有更大的意义。
规范里提到 Java语言有一个机制叫做:access control。在入门 Java的时候,我写一个类的构造函数的时候是不带任何的access modifiers(public, protected, private)
的,例如:
/src/other/Person.java
package other;
public class Person {
public Person() {
System.out.println("I eat a(n) " + new Fruit("apple").getName() + " today.");
}
}
---
/src/another/Main.java
package another;
import other.*;
public class Main {
public static void main(String[] args) {
new Person();
}
}
在上面一例中,无论是构造方法还是 sleep
,都没有作用域修饰符,按照以往教材的叫法,那么这些方法默认是 friendly
的,但是现在我认为应该不是这么个叫法了。
In a normal class declaration, a constructor declaration with no access modifiers has package access.
If a top level class or interface type is declared with package access, then it may be accessed only from within the package in which it is declared.
A top level class or interface type declared without an access modifier implicitly has package access.
引用两段规范里的说法,可以提炼到关键概念: package access
,这个概念简单来说就是对于是package access
的类或者接口,我们在其他东西内要访问这些个类或者接口,就只能在同一个 package内进行。
加入我的主方法和上面的Person
不是同一个package,是不能在主方法内直接实例化,因为 a constructor declaration with no access modifiers has package access. 为了在主方法内可访问需要在Person
的constructor
前加上public
。现在加点新的,令两个文件内容分别为:
/src/another/Person.java
package other;
class Fruit {
String name;
Fruit(String fruitName) {
name = fruitName;
}
public String getName() {
return name;
}
}
public class Person {
public Person() {
System.out.println("I eat a(n) " + new Fruit("apple").getName() + " today.");
}
}
---
/src/other/Person.java
package another;
import other.*;
public class Main {
public static void main(String[] args) {
new Person();
//new Fruit();会失败
}
}
在 Person.java
内不能有两个 public
类,所以 Fruit
这个类就不能是 public
的,然后当试图在主方法内进行 Fruit
的实例化时,是不能成功的,因为 Fruit
类此时是个 package access
的状态,且该类与主类不在同一个包内,故不能实例化,哪怕该类的构造方法和 Person
一样尝试改为 public
也是行不通的。
再来一个例子,在与 Fruit
同一个包内创建一个 Farmer
类:
/src/other/Farmer.java
package other;
import org.junit.jupiter.api.Test;
public class Farmer {
public Farmer() { }
@Test
void saySomething() {
System.out.println("I plant many " + new Fruit("watermelon").getName());
}
}
console:
I plant many watermelon
由于 Farmer
和 Fruit
在同一个 package下,所以即使 Fruit
本身以及其构造方法不是public
的,Farmer
仍然可以实例化 Fruit
类。
后话
- 其余的一些默认行为,例如:接口的所有成员如果没有
access modifier
,那么是隐式为public
。 - 为什么一门语言要设立这些可访问性的概念呢?很大一个原因就是
to prevent the users of a package or class from depending on unnecessary details of the implementation of that package or class.
也就是说有些代码写出来是为了我自个儿用来再实现别的功能的,我不想把它直接暴露给其他的用户,而是要极力隐藏它,因为其他的用户一旦可以访问这些代码,可能会因为自己对这些代码片面的认识而不能正确使用,并在直接调用后引发意料之外的结果。 - 另外,用好这个可访问性的概念,有助于编排好自己的设计构想,使之更加严谨,例如要写一个单例模式,其中要做的一个就是把构造方法
private
化。