Java 在涉及变量的情况下,关于重写的轻微混淆

Java 在涉及变量的情况下,关于重写的轻微混淆,java,overriding,scjp,ocpjp,Java,Overriding,Scjp,Ocpjp,我正在准备SCJP(最近被甲骨文更名为OCPJP),一个我在模拟考试中出错的问题让我困惑,答案描述没有足够清楚地解释问题 问题是: class A { int x = 5; } class B extends A { int x = 6; } public class CovariantTest { public A getObject() { return new A(); } public static void m

我正在准备SCJP(最近被甲骨文更名为OCPJP),一个我在模拟考试中出错的问题让我困惑,答案描述没有足够清楚地解释问题

问题是:

class A 
{
    int x = 5;
} 
class B extends A 
{
    int x = 6;
} 
public class CovariantTest 
{
    public A getObject() 
    {
       return new A();
    } 
    public static void main(String[]args) 
    {
       CovariantTest c1 = new SubCovariantTest();
       System.out.println(c1.getObject().x);
    }
}

class SubCovariantTest extends CovariantTest 
{
    public B getObject() 
    {
       return new B();
    }
}
答案是
5
,但我选择了
6

我知道重写适用于运行时的方法,而不是变量,但我认为
println
的解释方式是:

  • 在c1上调用getObject
  • c1实际上是一个
    子对象test
    对象,并且具有有效的覆盖 对于
    getObject()
    ,请使用重写的方法
  • 覆盖返回B,所以从B抓取x,即6

  • 这是不是JVM忽略了
    getObject()
    部分,总是从
    c1
    中获取
    x
    作为编译时关联的变量?

    尽管对suboviantTest正确地完成了覆盖,但由于变量c1的声明方式,答案是5。它被声明为协变项,而不是次协变项

    当运行c1.getObject().x时,它不知道这是一个测试(没有使用强制转换)。这就是为什么协变项返回5,而不是次协变项测试返回6

    如果你改变

    System.out.println(c1.getObject().x);
    

    你会得到你期望的6分

    编辑:正如评论中指出的那样


    “在Java中,字段不是多态的。只有方法是多态的。子类中的x隐藏基类中的x。它不会覆盖它。”(感谢JB Nizet)

    您正在从
    c1
    调用方法:
    System.out.println(c1.getObject().x)

    c1参考类型为:

    public class CovariantTest 
    {
        public A getObject() 
        {
           return new A();
        } 
        public static void main(String[]args) 
        {
           CovariantTest c1 = new SubCovariantTest();
           System.out.println(c1.getObject().x);
        }
    }
    

    因此:
    c1.getObject()
    返回类型是
    A
    。从
    A
    直接获取属性而不是方法,正如您提到的,java不会覆盖属性,因此它从
    A

    获取
    x
    ,这里发生的技术术语是“隐藏”。Java中的变量名称由引用类型解析,而不是由它们引用的对象解析

    • 对象具有A.x变量
    • B对象同时具有A.x和B.x变量
    但是,具有相同签名的实例方法是“重写的”而不是“隐藏的”,并且您无法访问从外部重写的方法的版本

    请注意,隐藏也适用于具有相同签名的静态方法

    简化形式的模拟问题(无覆盖):


    好吧,我知道现在回答这个问题有点晚了,但我和我的朋友也有同样的问题,这里已经给出的答案对我们来说还不太清楚。因此,我只想说明我遇到了什么问题,以及它现在的意义:)

    现在我明白了字段不会被重写,而是像miller.bartek指出的那样被隐藏,我也明白了重写是针对方法的,而不是Scott指出的字段

    然而,我遇到的问题是这个。据我说,

    c1.getObject().x
    
    这必须转化为:

    new B().x     // and not newA().x since getObject() gets overrided
    
    结果是6

    我不明白为什么类A(超级类)的变量会被类B(子类)的对象调用,而没有明确要求这样的行为

    从问题的措辞猜,我觉得OP也有同样的问题/疑问


    我的答覆是: 你从埃尔贝克的回答中得到了一个暗示。在main方法中放入以下行并尝试编译代码:

    A a = c1.getObject();    //line 1
    B b = c1.getObject();    //line 2
    
    您会注意到第1行是完全合法的,而第2行给出了编译错误

    因此,当调用函数getObject()时,协变(super)函数将被subcoveriantTest(sub)函数覆盖,因为这在代码中是有效的覆盖,c1.getObject()将返回新的B()

    但是,由于超级函数返回类类型a的引用,即使在被重写之后,它也必须返回类类型a的引用,当然,除非我们对其进行类型转换。这里,类B是类a(由于继承)

    实际上,我们从c1.getObject()得到的不是

    但这是:

    (A) new B()
    

    这就是为什么即使返回了类B的对象,并且类B的值x为6,输出结果仍然是5。

    重写方法时,会调用子类方法,当变量被重写时,当子类和父类都有一个同名变量时,将使用超类变量。子类的变量隐藏父类的变量,这称为变量隐藏

    虽然变量隐藏看起来像是重写一个变量,类似于方法重写,但事实并非如此,重写只适用于方法,而隐藏是适用于变量的

    在方法重写的情况下,重写的方法完全替换继承的方法,因此当我们试图通过持有子对象从父引用访问方法时,将调用子类中的方法

    但是在变量隐藏中,子类隐藏继承的变量,而不是替换,所以当我们试图通过持有子对象从父引用访问变量时,它将从父类访问

    如果子类中的实例变量与超类中的实例变量同名,则从引用类型中选择实例变量


    你可以阅读更多关于我的文章

    你确定吗?我很确定多态性的全部要点是能够做到这样:
    Paint[]Paint={new RedPaint(),new BluePaint(),new GreenPaint()};'然后为(Paint p:Paint)Paint.getColor();`其中,
    Paint
    的每个子类都有一个对
    getColor()
    的有效重写。无论是否知道子类或是否需要对其进行强制转换,每个字段都应该执行不同的操作。只有方法是有效的。子类中的x隐藏了th中的x
    A a = c1.getObject();    //line 1
    B b = c1.getObject();    //line 2
    
    new B()
    
    (A) new B()