是时候抛弃掉那些中文Java教材里所谓的friendly概念了

前言

曾经学 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. 为了在主方法内可访问需要在Personconstructor前加上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

由于 FarmerFruit在同一个 package下,所以即使 Fruit本身以及其构造方法不是public的,Farmer仍然可以实例化 Fruit类。


后话

  1. 其余的一些默认行为,例如:接口的所有成员如果没有access modifier,那么是隐式为 public
  2. 为什么一门语言要设立这些可访问性的概念呢?很大一个原因就是 to prevent the users of a package or class from depending on unnecessary details of the implementation of that package or class. 也就是说有些代码写出来是为了我自个儿用来再实现别的功能的,我不想把它直接暴露给其他的用户,而是要极力隐藏它,因为其他的用户一旦可以访问这些代码,可能会因为自己对这些代码片面的认识而不能正确使用,并在直接调用后引发意料之外的结果。
  3. 另外,用好这个可访问性的概念,有助于编排好自己的设计构想,使之更加严谨,例如要写一个单例模式,其中要做的一个就是把构造方法 private化。
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值