简体   繁体   English

为什么将受保护的访问修饰符与静态属性一起使用与非静态属性一起使用时会有所不同

[英]Why protected access modifier works differently when it's used along with static from that used with non static

Normally it is said that when we are using protected for a field in a class then its subclass cannot access it using a reference of the Base Class given that the subclass is in a different package . 通常,当我们在类中为某个字段使用protected时,假设该子类位于不同的包中,则其子类无法使用基类的引用来访问它。 That is true . 那是真实的 。 But I found that it behaves differently when a static keyword is added with the field . 但是我发现,在字段中添加static关键字时,它的行为有所不同。 It becomes accessible . 它变得可访问。 How is it possible . 这怎么可能 。 Is any one having the answer . 有谁能回答。

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);
    }
}

6.6.2.1. 6.6.2.1。 Access to a protected Member 访问受保护的成员

Let C be the class in which a protected member is declared. 令C为声明受保护成员的类。 Access is permitted only within the body of a subclass S of C. 仅在C的子类S的主体内允许访问。

In addition, if Id denotes an instance field or instance method, then: 另外,如果Id表示实例字段或实例方法,则:

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

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

see 看到

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

is valid because new BMW() is a subclass of Car, even being in a different package. 之所以有效,是因为new BMW()是Car的子类,甚至位于不同的包装中。

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

is not valid because new Car() is not a subclass of Car, and it's being called in a different package. 是无效的,因为new Car()不是Car的子类,并且在另一个包中被调用。 (see Java: Is a class a subclass of itself? for a discussion if a class is subclass of itself) (请参阅Java:类是其自身的子类吗?有关类是否是其自身的子类的讨论)

Now, if carNo is static, this is legal 现在,如果carNo是静态的,这是合法的

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

However, the right syntax here would be 但是,正确的语法是

System.out.println(Car.carNo);

because carNo is not an instance field , since it's static. 因为carNo不是实例字段 ,因为它是静态的。 In fact, even this will work from inside BMW 实际上,即使这样也可以在宝马内部使用

System.out.println(carNo);

because 因为

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 只有声明为protected或public的类的成员才能被包中声明的子类继承,而不是声明该包的子类

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

The main method is in BMW, that is a subclass of Car. 主要方法是在BMW中,这是Car的子类。 Thus, it has access to the protected variable. 因此,它可以访问受保护的变量。

The reason it was not visible before was because static methods, like main, cannot access non-static variables. 之所以之前不可见,是因为像main这样的静态方法无法访问非静态变量。 Once both criterias are fullfilled, the main method can access it. 满足两个条件后,主要方法即可访问它。

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
    }
}

Two ways to remove it: either make a static 删除它的两种方法:使其成为静态

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
    }
}

or call it outside main in a non static method, main being static cannot call to class variables 或以非静态方法在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) {

    }
}

You are mixing two concepts here: 您在这里混合了两个概念:
1) accessing static variables from non-static context 1)从非静态上下文访问静态变量
2) protected access modifier 2)受保护的访问修饰符
in java you can access protected members through inheritance or only within the same package . 在Java中,您可以通过继承或仅在同一包中访问受保护的成员

Try accessing noCar here: 尝试在此处访问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
    }
}

EDIT: considering packages 编辑:考虑包

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;
    }

}

The reason being the keyword "static". 原因是关键字“静态”。

Static associates the variable with the class and the not the instance. 静态将变量与类而不是实例相关联。 Since the class is public , all it's static variables will also be public ie all the variables will be accessible from other classes. 由于该类是public,因此它的所有静态变量也将是public,即所有变量都可以从其他类访问。

Also BMW extendsCar. 宝马也扩展了汽车。 Hence it will always be visible to BMW. 因此,它将始终对BMW可见。

Yes, that is weird. 是的,那很奇怪。 In order to shed some light on this behaviour, it may be helpful, first of all, to recap how the protected modifier works in the absence of the static keyword, and why it works the way it does. 为了阐明这种行为,首先,回顾一下在没有static关键字的情况下,protected修饰符的工作方式以及其工作原理,可能会有所帮助。

If class T declares a protected member m, then T and any class belonging to the same package as T can access the member, ie can say tm; 如果类T声明了受保护的成员m,则T和与T属于同一包的任何类都可以访问该成员,即可以说tm; the type of the reference (t) must be T or a subclass of T. Additionally, any subclass U of T outside T's package can say tm; 引用的类型(t)必须是T或T的子类。此外,在T包之外的T的任何子类U可以说tm; in this case, the type of t must be U or a subclass of U. 在这种情况下,t的类型必须为U或U的子类。

The final part of this statement contains an important restriction. 该声明的最后一部分包含一个重要限制。 Its motivation is succintly explained in section 3.5 of The Java Programming Language (fourth edition) by Arnold, Gosling and Holmes: 在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. 如果一个不同的子类可以访问第一个子类的受保护成员,则它可以以破坏第一个子类的契约的方式来操作它们-这是不允许的。

Let's try to better understand this explanation by putting your Car and BMW classes to work. 让我们将您的Car和BMW班级投入使用,以尝试更好地理解这一解释。 The following is a modified version of Car. 以下是Car的修改版本。 I replaced the carNo field with an equally protected, but non-static, color field. 我用同样受保护但非静态的色域替换了carNo字段。 The class also declares an obvious public getter/setter pair. 该类还声明了一个明显的公共获取器/设置器对。

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;
    }
}

Here is the BMW class, which at the moment does nothing but inherit Car's members. 这是BMW类别,目前该类别只继承Car的成员。

package com.bmw;

import com.car.Car;

public class BMW extends Car {
}

Finally, let's add another subclass of 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.");
    }

    ...
}

As you can see, in our application Ferrari objects show an exclusive preference for a certain color (which is almost true in the real world, too). 如您所见,在我们的应用程序中,法拉利对象显示了对特定颜色的排他性偏好(在现实世界中也是如此)。 The color field is set in the constructor, and made read-only by a straightforward override of setColor(). color字段在构造函数中设置,并通过直接覆盖setColor()使其变为只读。 Notice, by the way, that direct access to the color protected member is permitted here because the reference (an implicit this) is of the right type, that of the accessing subclass (Ferrari plays the role of U in the above description). 注意,顺便说一下,这里允许直接访问受颜色保护的成员,因为引用(隐式此引用)是正确类型,即访问子类的引用(在以上描述中,Ferrari扮演U的角色)。

Now, suppose that BMW objects want to demonstrate their superiority over other cars, so they ask the class's programmer to be enhanced by means of a bold overtake() method. 现在,假设BMW对象想要展示其相对于其他汽车的优越性,因此他们要求通过大胆的overtake()方法增强类的程序员。 The programmer obliges. 程序员有义务。

...

public class BMW extends Car {

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

    ...
}

But, reading the application logs, the programmer soon discovers that, while this works fine with other cars, Ferrari objects stubbornly resist any insolence. 但是,通过阅读应用程序日志,程序员很快发现,虽然这在其他汽车上也能正常工作,但法拉利的物体却顽强地抵抗了任何野蛮行为。 He then, urged by BMW objects to find a solution, tries to by-pass the setColor() method... 然后,在宝马对象的敦促下,他试图绕过setColor()方法...

...

public class BMW extends Car {

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

    ...
}

... which is exactly what we can't do in Java. ...这正是我们在Java中无法做到的。 The Ferrari subclass's expanded contract places a constraint on the value of the color protected member. 法拉利子类的扩展合同对受颜色保护的成员的价值施加了约束。 If the BMW subclass could directly access the color field through a Car (or Ferrari) reference, it would be able to break that contract. 如果BMW子类可以通过Car(或Ferrari)参考直接访问色域,则可以打破该合同。 Java does not allow this. Java不允许这样做。

So, this is why the protected modifier behaves the way it does when applied to non-static members. 因此,这就是为什么protected修饰符应用于非静态成员时会表现出其行为的原因。 With protected static members, things change altogether. 使用受保护的静态成员,情况会完全改变。 If the color field were static, any method inside BMW could directly access it. 如果颜色字段是静态的,那么BMW内部的任何方法都可以直接访问它。 In your code, the BMW class accesses the carNo field without a hitch. 在您的代码中,BMW类无障碍地访问carNo字段。

In the example above, the Ferrari class can restrict the possible values of the color field by overriding the instance setColor() method, which effectively amounts to changing, without violating, the superclass's contract. 在上面的示例中,Ferrari类可以通过重写实例setColor()方法来限制色域的可能值,这实际上等于更改而不违反超类的协定。

Now, Java is, by design, a class-based object-oriented language, which does not have a concept of class object in the same sense as, for example, Objective-C. 现在,从设计上来说,Java是一种基于类的面向对象的语言,该类没有像例如Objective-C一样具有类对象的概念。 In Objective-C, classes are, literally, objects, and class methods (analogous, but non identical, to Java static methods) are, so to speak, instance methods of the class object -- with all the consequences of this fact: in particular, they can be overridden and used as polymorphic operations, the array class method in NSArray and NSMutableArray being an obvious example. 在Objective-C中,类实际上是对象,而类方法(与Java静态方法类似但不完全相同)可以说是类对象的实例方法-具有这个事实的所有后果:特别是,它们可以被覆盖并用作多态操作,NSArray和NSMutableArray中的数组类方法就是一个明显的例子。

In Java, there is no class object -- an instance of java.lang.Class is by no means the same thing as an Objective-C class object. 在Java中,没有类对象-java.lang.Class的实例与Objective-C类对象绝不是同一回事。 Static methods are, in essence, functions with an associated namespace. 本质上,静态方法是具有关联名称空间的函数。 Most importantly, they can be inherited, but can't be overridden -- only hidden, just like static and non-static fields. 最重要的是,它们可以被继承,但不能被覆盖-只能被隐藏,就像静态和非静态字段一样。 (By the way, this means that invoking a static method is more efficient than calling an instance method, because not only its form, but also its implementation can be chosen at compile-time.) (顺便说一句,这意味着调用静态方法比调用实例方法更有效,因为不仅可以在编译时选择其形式,而且可以选择其实现。)

But, and this is the end of the story, if static members cannot be overridden, they cannot really change the superclass's contract either. 但是,这就是故事的结局,如果不能重写静态成员,那么它们也不能真正更改超类的合同。 And, if they cannot change the superclass's contract, a subclass cannot break the contract of a different subclass by only accessing the latter's static members. 并且,如果子类不能更改超类的契约,则子类不能仅通过访问其子类的静态成员来破坏其他子类的契约。 If we recall that avoiding this kind of violations was precisely the reason of the restriction concerning protected non-static members, we can now understand why the designers of Java ended up lifting that restriction for protected static members. 如果我们记得避免这种违规行为恰恰是对受保护的非静态成员进行限制的原因,那么我们现在可以理解,为什么Java的设计者最终对受保护的静态成员取消了该限制。 Once again, we can find a concise allusion to this line of thought in a short passage from section 3.5 of The Java Programming Language : 再次,我们可以在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. 可以在任何扩展类中访问受保护的静态成员...这是允许的,因为子类无法修改其静态成员的协定,因为它只能隐藏它们,而不能覆盖它们-因此,没有其他类的危险违反合同。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 为什么不将私有,静态,受保护的访问修饰符与类一起使用? - Why private, static, protected access modifers are not used with a class? 为什么在静态上下文中无法引用非静态方法时,String :: isEmpty有效? - Why does String::isEmpty works when non-static method cannot be referenced from a static context? 静态方法的受保护修饰符的目的是什么 - What is the purpose of protected modifier for static methods 受保护的静态方法访问 - Protected Static Method Access 为什么“final static int”可以用作switch的case常量而不是“final static <your enum>” - Why “final static int” can be used as a switch's case constant but not “final static <your enum>” 可以使用getClass()方法访问静态变量吗? - Can the getClass() method be used to access static variables? 在使用静态初始化之前如何加载Java类? - how to load a java class along with static initializations before they are used? 方法引用可以用于访问静态方法吗? - Can method references be used to access static methods? 为什么接口中的静态方法不需要默认的访问修饰符? - Why doesn't the static method in an interface not need a default access modifier? Java的默认访问修饰符曾经是公开的 - Did Java's default access modifier used to be public
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM