有 Java 编程相关的问题?

你可以在下面搜索框中键入要查询的问题!

java为什么受保护的访问修饰符在与静态一起使用时与与与非静态一起使用时的工作方式不同

通常情况下,当我们对类中的字段使用protected时,如果子类位于不同的包中,则其子类无法使用基类的引用访问它。这是真的。但我发现,在字段中添加静态关键字时,它的行为会有所不同。它变得容易接近。这怎么可能。有人知道答案吗

package com.car;

public class Car {

    static protected int carNo=10;

}


package com.bmw;
import com.car.*;

public class BMW extends Car {

    public static void main(String[] args) {
        //Its accessible here
        System.out.println(new Car().carNo);
    }
}

共 (5) 个答案

  1. # 1 楼答案

    主要的方法是宝马,这是汽车的一个子类。因此,它可以访问受保护的变量

    它以前不可见的原因是,静态方法(如main)无法访问非静态变量。一旦两个标准都被填满,主方法就可以访问它

  2. # 2 楼答案

    原因是关键字“静态”

    静态将变量与类和实例相关联,而不是与实例相关联。由于该类是公共的,所以它的所有静态变量也将是公共的,即所有变量都可以从其他类访问

    宝马也扩展了汽车。因此,它将始终可见宝马

  3. # 3 楼答案

    6.6.2.1. Access to a protected Member

    Let C be the class in which a protected member 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.

    资料来源:https://docs.oracle.com/javase/specs/jls/se7/html/jls-6.html#jls-6.6.2.1

    public class BMW extends Car {
        public static void main(String[] args) {
            System.out.println(new BMW().carNo);
        }
    }
    

    是有效的,因为new BMW()是Car的一个子类,即使是在不同的包中

    public class BMW extends Car {
        public static void main(String[] args) {
            System.out.println(new Car().carNo);
        }
    }
    

    无效,因为new Car()不是Car的子类,并且正在另一个包中调用它。(如果一个类是其自身的子类,请参见Java: Is a class a subclass of itself?了解讨论)

    现在,如果卡诺是静态的,这是合法的

    System.out.println(new Car().carNo);
    

    然而,这里正确的语法应该是

    System.out.println(Car.carNo);
    

    因为carNo不是一个实例字段,因为它是静态的。事实上,即使这样,宝马内部也会起作用

    System.out.println(carNo);
    

    因为

    Only members of a class that are declared protected or public are inherited by subclasses declared in a package other than the one in which the class is declared

    https://docs.oracle.com/javase/specs/jls/se7/html/jls-8.html#jls-8.2所述

  4. # 4 楼答案

    是的,这很奇怪。为了阐明这种行为,首先,回顾一下在没有static关键字的情况下受保护的修饰符是如何工作的,以及为什么它以这种方式工作,可能会有所帮助

    如果类T声明了一个受保护的成员m,那么T和属于与T相同包的任何类都可以访问该成员,即可以说T.m;引用(t)的类型必须是t或t的一个子类。此外,t包外的任何t的子类U都可以称为t.m;在这种情况下,t的类型必须是U或U的子类

    本声明的最后部分包含一个重要限制。Arnold、Gosling和Holmes在Java编程语言(第四版)的第3.5节中简要解释了它的动机:

    The reasoning behind the restriction is this: Each subclass inherits the contract of the superclass and expands that contract in some way. Suppose that one subclass, as part of its expanded contract, places constraints on the values of protected members of the superclass. If a different subclass could access the protected members of objects of the first subclass then it could manipulate them in a way that would break the first subclass's contract -- and this should not be permissible.

    让我们试着更好地理解这个解释,把你的汽车和宝马类工作。以下是汽车的改进版。我用一个同样受保护但非静态的颜色场替换了carNo场。该类还声明了一个明显的公共getter/setter对

    package com.car;
    
    import java.awt.Color;
    
    public class Car {
    
        protected Color color;
    
        public Color getColor() {
            return color;
        }
    
        public void setColor(Color color) {
            this.color = color;
        }
    }
    

    这是宝马级,目前它只继承汽车的成员

    package com.bmw;
    
    import com.car.Car;
    
    public class BMW extends Car {
    }
    

    最后,让我们添加Car的另一个子类

    package com.ferrari;
    
    import com.car.Car;
    import java.awt.Color;
    
    public class Ferrari extends Car {
    
        public Ferrari() {
            color = Color.RED;
        }
    
        @Override
        public void setColor(Color color) {
            log("Nope. I'm proud of my color.");
        }
    
        ...
    }
    

    正如您所见,在我们的应用程序中,法拉利物体显示出对某种颜色的独有偏好(在现实世界中几乎也是如此)。颜色字段在构造函数中设置,并通过直接重写setColor()使其成为只读。顺便说一下,请注意,这里允许直接访问受颜色保护的成员,因为引用(隐式this)是正确的类型,即访问子类的类型(法拉利在上述描述中扮演U的角色)

    现在,假设BMW对象想要展示其相对于其他汽车的优势,因此他们要求类的程序员通过一个bold TRACKET()方法进行增强。程序员很乐意

    ...
    
    public class BMW extends Car {
    
        public void overtake(Car car) {
            log("Wow! Become green with envy!");
            car.setColor(Color.GREEN);
        }
    
        ...
    }
    

    但是,在阅读应用程序日志后,程序员很快发现,尽管这在其他汽车上运行良好,法拉利的物体却顽固地抵制任何傲慢。然后,在BMW objects的敦促下,他试图绕过setColor()方法

    ...
    
    public class BMW extends Car {
    
        public void overtake(Car car) {
            log("Wow! Become green with envy!");
            car.color = Color.GREEN;    // <-
        }
    
        ...
    }
    

    。。。这正是我们在Java中无法做到的。法拉利子类的扩展合同对受颜色保护的成员的值设置了约束。如果BMW子类可以通过汽车(或法拉利)引用直接访问颜色字段,它将能够打破该合同。Java不允许这样做

    因此,这就是为什么受保护修改器在应用于非静态成员时的行为方式。使用受保护的静态成员,情况会完全改变。如果颜色字段是静态的,BMW内部的任何方法都可以直接访问它。在您的代码中,BMW类可以顺利访问carNo字段

    在上面的示例中,Ferrari类可以通过重写实例setColor()方法来限制颜色字段的可能值,这实际上相当于在不违反超类约定的情况下更改

    现在,Java从设计上讲是一种基于类的面向对象语言,它没有与Objective-C相同意义上的类对象概念。在Objective-C中,类从字面上来说是对象,类方法(与Java静态方法类似但不完全相同)可以说是,类对象的实例方法——以及这一事实的所有后果:特别是,它们可以被重写并用作多态操作,NSArray和NSMutableArray中的数组类方法就是一个明显的例子

    在Java中,没有类对象——Java的实例。类与Objective-C类对象绝不是一回事。静态方法本质上是具有关联命名空间的函数。最重要的是,它们可以被继承,但不能被覆盖——只能被隐藏,就像静态和动态的一样非静态字段。(顺便说一句,这意味着调用静态方法比调用实例方法更有效,因为编译时不仅可以选择其形式,还可以选择其实现。)

    但是,故事到此结束,如果静态成员不能被覆盖,它们也不能真正更改超类的契约。而且,如果子类不能更改超类的约定,那么它就不能通过只访问另一个子类的静态成员来打破另一个子类的约定。如果我们还记得避免这种违规行为正是限制受保护的非静态成员的原因,那么我们现在可以理解为什么Java的设计者最终取消了对受保护的静态成员的限制。同样,我们可以在Java编程语言第3.5节的一篇短文中找到对这一思路的简明暗示:

    Protected static members can be accessed in any extended class... This is allowed because a subclass can't modify the contract of its static members because it can only hide them, not override them -- hence, there is no danger of another class violating that contract.

  5. # 5 楼答案

    class Car {
       protected int a = 9;
    }
    
    class BMW extends Car{
        public static void main(String[] args) {
          int b = a; // cannot make a static reference to a non static field warning error shown by eclipse
        }
    }
    

    有两种方法可以删除它: 使a静态

    class Car {
       protected static int a = 9;
    }
    
    class BMW extends Car{
        public static void main(String[] args) {
        int b = a; // cannot make a static reference to a non static field
        }
    }
    

    或者在非静态方法中在main外部调用它,main是静态的,不能调用类变量

    class Car {
       protected static int a = 9;
    }
    
    class BMW extends Car{
        public void m() {
        int b = a; 
        }
        public static void main(String[] args) {
    
        }
    }
    

    您在这里混合了两个概念:
    1) 从非静态上下文访问静态变量
    2) 受保护的访问修饰符
    在java中,您可以通过继承或仅在同一个包中访问受保护的成员

    请尝试在此处访问noCar:

    class Car{
        int noCar = 9;
        public static void main(String[] args) {
          int b = noCar; // cannot make a static reference to a non static field warning error shown by eclipse
        }
    }
    

    编辑:考虑软件包

    package com.bmw;
    
    import com.car.*;
    
    public class BMW extends Car {
    
        public static void main(String[] args) {
           System.out.println(new BMW().carNo);
           Car car = new Car();
           // Car has no idea that BMW is the child class
           // and since it is not public we cannot access it directly
            //can be accessed like this
            car.getCarNo();
           // you can do this because BMW has the variable carNo because of it extending Car
           BMW bmw = new BMW();
           int a = bmw.carNo;
        }
    }
    
    package com.car;
    
    public class Car {
    
        protected int carNo=10;
        public int getCarNo() {
            return carNo;
        }
    
        public void setCarNo(int carNo) {
            this.carNo = carNo;
        }
    
    }