Java中的protected和package-private访问修饰符之间的区别?

difference between protected and package-private access modifiers in Java?

本问题已经有最佳答案,请猛点这里访问。

我已经看到了关于受保护修饰符和包私有修饰符之间差异的各种文章。我发现这两个职位之间有一点矛盾

  • "package private"成员访问是否与默认(无修饰符)访问同义?

    在这个问题上,公认的答案是


    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.

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

    在这个问题上,公认的答案是


    To satisfy protected level access two conditions must be met:

    • The classes must be in the same package.
    • There must be an inheritance relationship.
  • 它们不是矛盾的吗?根据我对其他文章的理解,第一篇文章给出了正确的答案,即protected==package private+other package中的子类。

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

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

    我的super和subclass代码如下。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    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();
            }
        }

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


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

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

    但是,有一个小技巧:

    6.6.2 Details on protected Access

    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.

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

    例如(假设此代码在Cat中):

    1
    2
    3
    4
    5
    6
    7
    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

    这是有意义的,因为通过Cat访问Dogprotected成员可能破坏Dog的不变量,而Cat可以安全访问自己的protected成员,因为它知道如何确保自己的不变量。

    细则:

    6.6.2.1 Access to a protected Member

    Let C be the class in which a protected member m is declared. Access is permitted only within the body of a subclass S of C. In addition, if Id denotes an instance field or instance method, then:

    • If the access is by a qualified name Q.Id, where Q is an ExpressionName, then the access is permitted if and only if the type of the expression Q is S or a subclass of S.
    • If the access is by a field access expression E.Id, where E is a Primary expression, or by a method invocation expression E.Id(. . .), where E is a Primary expression, then the access is permitted if and only if the type of E is S or a subclass of S.

    6.6.2.2 Qualified Access to a protected Constructor

    Let C be the class in which a protected constructor is declared and let S be the innermost class in whose declaration the use of the protected constructor occurs. Then:

    • If the access is by a superclass constructor invocation super(. . .) or by a qualified superclass constructor invocation of the form E.super(. . .), where E is a Primary expression, then the access is permitted.
    • If the access is by an anonymous class instance creation expression of the form new C(. . .){...} or by a qualified class instance creation expression of the form E.new C(. . .){...}, where E is a Primary expression, then the access is permitted.
    • Otherwise, if the access is by a simple class instance creation expression of the form new C(. . .) or by a qualified class instance creation expression of the form E.new C(. . .), where E is a Primary expression, then the access is not permitted. A protected constructor can be accessed by a class instance creation expression (that does not declare an anonymous class) only from within the package in which it is defined.

    参见:

    • Java语言规范


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

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


    您已经创建了一个cat实例并将其强制转换为超级类类型,即动物类型。根据动物类型,它的TestInstanceMethod在同一个包或任何子类型中都可见。如果您没有强制转换为动物类型,代码将编译。

    希望有所帮助

    /阿伦