在 Java 中,为什么可以从同一个包内的 class 外部访问受保护的成员?

In Java, why can a protected member be accessed from outside the class within the same package?

Herbert Schildt 在他的书中第 172 页(第 3 段)中说“protected 仅在涉及继承时适用。”。

在第 228 页,Table9-1 表明可以从同一包中的非子 class 访问受保护的成员。

以下代码有效并支持 Table 9-1.

中的信息

Class1.java:

package Mypack;
public class Class1
{
    protected pro=1;
    public Class1()
    {
        System.out.println(pro);
    }
}

Class2.java

package Mypack;
class Class2 extends Class1
{
    Class2()
    {
        System.out.println(pro);
    }
}

Class3.java

package Mypack;
class Class3
{
    Class3()
    {
        Class1 class1=new Class1();
        System.out.println(class1.pro);
    }
}

可以从派生的classClass2访问变量pro,这很好。但是如何通过对Class1对象的引用从非派生classClass3访问它呢?它与第 172 页的声明相矛盾。如果是这样,那么我发现在这种情况下 public 和 protected 说明符之间没有区别。

声明的东西 protected is visible to itself, its children, and the package it's declared in.

如果 Class3 在不同的包中,例如 mypack.nested,那么您根本无法访问 class1.pro

The protected modifier specifies that the member can only be accessed within its own package (as with package-private) and, in addition, by a subclass of its class in another package. https://docs.oracle.com/javase/tutorial/java/javaOO/accesscontrol.html

In his book, Herbert Schildt says in page 172 (3rd paragraph) that "protected applies only when inheritance is involved.".

有一种说法认为该说法是正确的,尽管我认为它具有误导性。让我们看看来自 the access control tutorial:

的访问图表
Modifier    Class  Package  Subclass  World
public        Y       Y        Y       Y
protected     Y       Y        Y       N
no modifier   Y       Y        N       N
private       Y       N        N       N

请注意,无修饰符 授予成员 class 和包访问权限,但不授予子class 成员或整个世界的访问权限。 protected 只改变其中一件事情:它使成员对 subclasses 可用。所以从这个意义上说,他是对的:它只适用于涉及继承的情况;没有继承就等于没有修饰符

但我发现它很有误导性,因为正是这个原因激发了您的问题:这似乎暗示不会有包访问权限。如果您已经知道 no modifier 授予包访问权限,那么该语句唯一有意义的方法是。

为清楚起见:protected 表示包 中的任何 class 成员都可以在子 class 中编码。这样做可以让库拥有您只能从属于库的一部分的代码访问的字段和方法* (有点,见下文) 或有助于在库中实现某些内容的代码(例如,如果您从 classes 图书馆之一订阅 class)。除了语言的设计方式外,没有特别的“为什么”。

If it is so, then I find no difference between the public and protected specifiers in this situation.

在这种情况下,没有。但是,当您考虑 不是 在同一包中并且不在包成员的派生 class 中的代码时,显然存在很大差异:该代码无法访问 protected 个成员。

这在 JLS§6.6.1:

中有介绍

...if the member or constructor is declared protected, then access is permitted only when one of the following is true:

  • Access to the member or constructor occurs from within the package containing the class in which the protected member or constructor is declared.

  • Access is correct as described in §6.6.2.

(注意第一个项目符号)和 JLS§6.6.2:

A protected member or constructor of an object may be accessed from outside the package in which it is declared only by code that is responsible for the implementation of that object.

(“负责实现该对象的代码”——例如,子class中的代码。)


* Re my "sort of, see below" on "这样做可以让库拥有只能从代码访问的字段和方法那是库的一部分..." 这不是真的,因为除了受限的包(例如 java.lang),你可以愉快地写你自己的 class 说它在库的包,然后使用库的 classes 的包级字段和方法。 Java 的包概念不是 field/method 安全机制。

此 link 上的 table 显示修饰符与可以访问的内容 http://docs.oracle.com/javase/tutorial/java/javaOO/accesscontrol.html。然后根据需要向其他人隐藏的内容等因素选择修饰符 class 根据封装等良好原则需要共享的内容

访问级别

修改器Class包子class世界

public Y Y Y Y

受保护 Y Y Y N

无修饰符 Y Y N N

私人 Y N N N

If it is so, then I find no difference between the public and protected specifiers in this situation

没错,在那种情况下没有区别。
考虑这段代码:

package otherpack;
public class Class4
{
    Class4()
    {
        Class1 class1=new Class1(); // Ok, Class1 and it's constructor are public
        System.out.println(class1.pro); // Compilation error. pro is protected
    }
}

package otherpack;
public class Class5 extends Class1
{
    Class5()
    {
        Class1 class1=new Class1(); // Ok, Class1 and it's constructor are public
        System.out.println(class1.pro); // OK, Class5 extends Class1 and pro is protected
    }
}

在代码不同包的情况下public和protected是不同的。您可以使用来自不同包的 public 成员,但不能使用受保护的成员,除非您在派生的 class 中这样做。