Java中受保护访问修饰符和包私有访问修饰符之间的区别?

2022-09-01 03:20:33

我看过各种关于受保护和包私有修饰符之间差异的文章。我发现这两个帖子之间有矛盾的一件事

  1. “包私有”成员访问不是默认(无修饰符)访问的同义词吗?

    在这一点上,被接受的答案说

    受保护的修饰符指定只能在其自己的包中访问成员(与包专用一样),此外,还可以由另一个包中其类的子类访问。

  2. 为什么受保护的修饰符在 Java 子类中的行为不同?

    在这一点上,被接受的答案说

    要满足受保护级别访问,必须满足两个条件:

    • 这些类必须位于同一包中。
    • 必须存在继承关系。

它们不是自相矛盾吗?根据我对其他文章的理解,第一篇文章给出了正确的答案,即受保护的==包私有+其他包中的子类。

如果此语句正确,则为什么此代码失败,并在第 17 行的子类 Cat 上显示以下错误消息

The method testInstanceMethod() from the type Animal is not visible 

我的超类和子类代码如下。

package inheritance;

public class Animal {

    public static void testClassMethod() {
        System.out.println("The class" + " method in Animal.");
    }
    protected void testInstanceMethod() {
        System.out.println("The instance " + " method in Animal.");
    }
}

package testpackage;

import inheritance.Animal;

public class Cat extends Animal{
        public static void testClassMethod() {
            System.out.println("The class method" + " in Cat.");
        }
        public void testInstanceMethod() {
            System.out.println("The instance method" + " in Cat.");
        }

        public static void main(String[] args) {
            Cat myCat = new Cat();
            Animal myAnimal = myCat;
            myAnimal.testClassMethod();
            myAnimal.testInstanceMethod();
        }
    }

请澄清上述代码失败的原因。这将是非常有用的。谢谢


答案 1

第一个答案基本上是正确的 - 成员可以通过以下方式访问protected

  • 来自同一包的类
  • 来自其他包的声明类的子类

但是,有一个小技巧:

6.6.2 有关受保护访问的详细信息

可以从包外部访问对象的受保护成员或构造函数,在该包中,仅由负责实现该对象的代码声明该对象。

这意味着来自其他包的子类无法访问其超类的任意实例的成员,它们只能在其自身类型的实例上访问它们(其中type是表达式的编译时类型,因为它是编译时检查)。protected

例如(假设此代码位于):Cat

Dog dog = new Dog();
Animal cat = new Cat();

dog.testInstanceMethod(); // Not allowed, because Cat should not be able to access protected members of Dog
cat.testInstanceMethod(); // Not allowed, because compiler doesn't know that runtime type of cat is Cat

((Cat) cat).testInstanceMethod(); // Allowed

这是有道理的,因为访问 by 的成员可能会破坏 的不变量,而可以安全地访问自己的成员,因为它知道如何确保自己的不变量。protectedDogCatDogCatprotected

详细规则:

6.6.2.1 访问受保护的成员

设 C 是声明受保护成员 m 的类。只允许在 C 的子类 S 的主体内访问。此外,如果 Id 表示实例字段或实例方法,则:

  • 如果访问是通过限定名 Q.Id(其中 Q 是表达式名称)进行的,则当且仅当表达式 Q 的类型为 S 或 S 的子类时,才允许访问。
  • 如果访问是通过字段访问表达式 E.Id(其中 E 是主表达式)或通过方法调用表达式 E.Id(. .),其中 E 是主表达式,则当且仅当 E 的类型是 S 或 S 的子类时,才允许访问。

6.6.2.2 对受保护构造函数的限定访问

设 C 是声明受保护构造函数的类,让 S 是其声明中使用受保护构造函数的最内层类。然后:

  • 如果访问是通过超类构造函数调用 super(. . .) 或由 E.super(. . . ) 形式的限定超类构造函数调用(其中 E 是主表达式)进行的,则允许访问。
  • 如果访问是通过匿名类实例创建表达式 new C(. . .){...}或通过 E.new C(. . . ) 形式的限定类实例创建表达式{...},其中 E 是主表达式,则允许访问。
  • 否则,如果访问是通过 new C(. . .) 形式的简单类实例创建表达式或形式为 E.new C(. . .) 的限定类实例创建表达式(其中 E 是主表达式)进行的,则不允许访问。受保护的构造函数只能由类实例创建表达式(不声明匿名类)从定义它的包中访问。

另请参阅:


答案 2

在受保护访问中,成员在同一个包中访问,对于另一个包中的继承类成员,也可以访问。

在包访问中,可以访问同一包中类的成员。无法在包访问中访问其他包中的类成员。


推荐