我正在研究JAVA中的覆盖成员函数,并考虑过尝试覆盖成员变量。

因此,我定义了类

public class A{
    public int intVal = 1;
    public void identifyClass()
    {
        System.out.println("I am class A");
    }
}

public class B extends A
{
    public int intVal = 2;
    public void identifyClass()
    {
        System.out.println("I am class B");
    }
}

public class mainClass
{
    public static void main(String [] args)
    {
        A a = new A();
        B b = new B();
        A aRef;
        aRef = a;
        System.out.println(aRef.intVal);
        aRef.identifyClass();
        aRef = b;
        System.out.println(aRef.intVal);
        aRef.identifyClass();
    }
}


输出是:

1
I am class A
1
I am class B


我不明白为什么将aRef设置为b时intVal仍为A类?

评论

关于变量覆盖方面的轻微混淆的可能重复项

我认为这不是一个相同的问题,因为另一种语言混合了该语言的几个功能,而这个纯粹是关于变量不是多态的

@Vic Seedoubleyew但在我看来,这个问题像是重复的

类的Hiding实例变量的可能重复

#1 楼

当在子类中创建相同名称的变量时,这称为隐藏。现在,生成的子类实际上将具有这两个属性。您可以使用super.var((SuperClass)this).var从超类访问一个。变量甚至不必是同一类型。它们只是两个共享名称的变量,就像两个重载方法一样。

评论


因此,这样做并不是多态。它说明了OOP的“数据隐藏”概念吗?

–拉胡尔·拉斯托吉(Rahul Rastogi)
17年11月6日在7:34

@RahulRastogi不,与该概念无关。 “数据隐藏”是封装。

– Marko Topolnik
17年11月6日13:38



#2 楼

变量在Java中不是多态的。它们不会彼此覆盖。

评论


因此,由于不覆盖变量,因此不会对其执行运行时解析,因此在继承链中,访问时使用引用类的变量值代替对象类型。我通过进一步扩展类和使用中间引用类型来验证它。谢谢回复。

– Kalyan Raghu
2012年5月23日15:58



#3 楼

Java中的字段没有多态性。

Variables决定在编译时发生,因此始终将访问基类变量(而不是子代的继承变量)。

因此,每当进行向上转换时,请始终记住

1)将访问基类变量。

2)子类方法(如果发生覆盖则覆盖方法,否则将继承父类继承的方法)。

#4 楼

变量在编译时解析,方法在运行时解析。 aRef类型为A,因此aRef.Intvalue在编译时解析为1。

#5 楼

Java中的重写概念
函数将重写取决于对象类型,而变量将根据引用类型进行访问。


覆盖函数:在这种情况下,假设父类和子类都具有相同的具有自己定义的函数名称。但是执行哪个函数取决于对象类型,而不取决于运行时的引用类型。

例如:

Parent parent=new Child();
parent.behaviour();


这里parent是一个父类的引用但拥有子类的对象,因此这就是为什么在这种情况下将调用子类函数的原因。
Child child=new Child();
child.behaviour();


这里child拥有子类的对象,因此将调用Child类函数。

Parent parent=new Parent();
parent.behaviour();


parent拥有Parent类的对象,因此将调用Parent类函数。


覆盖变量:Java支持重载变量。但是实际上,这是两个具有相同名称的不同变量,一个在父类中,一个在子类中。并且这两个变量可以是相同的数据类型,也可以是不同的数据。

当您尝试访问变量时,它取决于引用类型的对象,而不是对象类型。

例如:

Parent parent=new Child();
System.out.println(parent.state);


引用类型是Parent,因此访问Parent类变量,而不是Child类变量。

Child child=new Child();
System.out.println(child.state);


此处引用类型为Child,因此不访问Parent类变量,而是访问Child类变量。

Parent parent=new Parent();
System.out.println(parent.state);


此处引用类型为Parent,因此可以访问父类变量。

评论


欢迎来到SO。请不要将整个答案格式化为代码。突出显示代码作为代码和文本作为文本。谢谢

– Neuron
'18 -4-1在14:38



#6 楼


摘自JLS Java SE 7 Edition§15.11.1:
由于缺少对字段访问的动态查找,因此可以通过简单的实现高效地运行程序。后期绑定和重载的功能是可用的,但是只有在使用实例方法时才可以使用。

Oliver Charlesworth和Marko Topolnik的回答是正确的,我想详细说明一下为什么问题:
在Java类中,成员是根据引用的类型而不是实际对象的类型来访问的。出于同样的原因,如果您在类someOtherMethodInB()中有一​​个B,则在运行aRef之后将无法从aRef = b访问它。标识符(即类,变量等名称)在编译时解析,因此编译器依赖于引用类型来执行此操作。
在您的示例中,当运行System.out.println(aRef.intVal);时,它会打印在intVal中定义的A的值,因为这是用于访问它的引用的类型。编译器看到aRef的类型为A,这就是它将要访问的intVal。不要忘记,在B实例中您同时拥有两个字段。如果想看一下,JLS也有一个与您类似的示例“ 15.11.1-1。字段访问的静态绑定”。
但是为什么方法的行为有所不同?答案是,对于方法,Java使用后期绑定。这意味着在编译时,它将找到最合适的方法在运行时进行搜索。搜索涉及方法在某些类中被覆盖的情况。

#7 楼

这称为变量隐藏。当您分配aRef = b;时,aRef有两个intVal,1命名为intVal,另一个隐藏在A.intVal下(请参见调试器屏幕截图),因为您的变量的类型为class A,即使您仅打印intVal,java也会智能地选择A.intVal

答案1:访问子类的intVal的一种方法是System.out.println((B)aRef.intVal);

答案2:另一种方法是Java反射:因为当您使用反射时,java不能智能地拾取隐藏的A.intVal基于类类型,它必须获取以字符串形式给出的变量名称-

import java.lang.reflect.Field;

class A{
    public int intVal = 1;
    public void identifyClass()
    {
        System.out.println("I am class A");
    }
}

class B extends A
{
    public int intVal = 2;
    public void identifyClass()
    {
        System.out.println("I am class B");
    }
}

public class Main
{
    public static void main(String [] args) throws Exception
    {
        A a = new A();
        B b = new B();
        A aRef;
        aRef = a;
        System.out.println(aRef.intVal);
        aRef.identifyClass();
        aRef = b;
        Field xField = aRef.getClass().getField("intVal");
        System.out.println(xField.get(aRef));
        aRef.identifyClass();
    }
}


输出-

1
I am class A
2
I am class B




#8 楼

希望对您有所帮助:

public class B extends A {
//  public int intVal = 2;

    public B() {
        super();
        super.intVal = 2;
    }

    public void identifyClass() {
        System.out.println("I am class B");
    }
}


因此无法重写基类的变量,但是可以从继承类的构造函数中设置(更改)基类变量的值。

评论


欢迎使用Stack Overflow!通常,如果答案包括对代码意图的解释,以及为什么在不引入其他代码的情况下解决问题的原因,则答案会更有帮助。

–蒂姆·迪克曼(Tim Diekmann)
18年5月25日在3:02

#9 楼

好吧,我希望你能得到答案。如果没有,您可以尝试在调试模式下查看。子类B可以同时访问intVal。它们不是多态的,因此不会被覆盖。

如果您使用B的引用,您将获得B的intVal。如果使用A的引用,则将获得A的intVal。就这么简单。

评论


在调试模式下,我看到b和aRef都具有intVal,而根据您的评论,我希望只有对象b才具有这两个值。是因为调试器的行为方式不同于JVM?

– Kalyan Raghu
2012年5月23日16:05

我想这是个误会。直到您没有将b分配给aRef时,它只有一个intVal。当您将b分配给aRef时,它现在指向类型B的对象,因此指向两个intVal。

–达兰
2012年5月23日16:09



#10 楼

根据Java规范,实例变量在扩展时不会被子类从超类覆盖。 。

另外,当在B的实例创建过程中调用A的构造函数时,变量(intVal)也会被初始化,从而输出。

评论


我没有得到第一点。我们始终可以访问子类中超类的任何非私有成员变量。

– Kalyan Raghu
2012年5月23日下午16:34

#11 楼

正如许多用户已经指出的那样,这不是多态。多态性仅适用于方法(函数)。
现在为什么要打印类A的intVal的值,发生这种情况是因为您可以看到引用aRef是A类型的。为什么你对它感到困惑。通过相同的过程,您已经访问了ex的重写方法。方法IdentityClass()而不是变量,它不能直接证明我写的第一行。超类可以有多个级别,例如
A <-B <-C。那就是C扩展了B,B扩展了A。如果您想要A的var值,那么就可以完成((A)c).var。
编辑:正如一位用户指出的那样'不适用于静态方法,因为它们是静态的。

评论


多态不适用于类方法

– Shamshirsaz.Navid
10月26日8:48

首先,我已经编辑了我的回复,感谢您指出。第二类方法更好地称为静态方法。最后,下次当您想到普通的低票表决时,请先让用户知道他/她犯了什么错误,如果没有纠正,请低票表决。这是我的第一个答案,现在我可以想象为什么许多人不回答。非常感谢您的鼓励。

–user420125
10月27日13:30

#12 楼

Java具有封装的特性,它紧密地绑定了对象的属性和行为。因此,只有通过类引用,我们才能调用它的行为来更改其属性。

在继承中,仅方法重写,因此它只能影响其属性。