java 成员变量 覆盖_覆盖Java中的成员变量(变量隐藏)

我正在研究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但是在我看来,这个问题像是重复的

隐藏类的实例变量的可能重复项

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

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

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

变量在Java中不是多态的。他们不会互相取代。

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

Java中没有字段的多态性。

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

因此,无论何时发生上流事件,都要记住

1)将访问基类变量。

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

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

Java中的超越概念

函数将根据对象类型进行覆盖,而变量将根据引用类型进行访问。

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

例如:

Parent parent=new Child();

parent.behaviour();

这里parent是Parent类的引用,但拥有Child类的对象,因此这就是在这种情况下将调用Child类函数的原因。

Child child=new Child();

child.behaviour();

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

Parent parent=new Parent();

parent.behaviour();

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,因此可以访问Parent类变量。

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

From JLS Java SE 7 Edition §15.11.1:

This lack of dynamic lookup for field accesses allows programs to be run efficiently with straightforward implementations. The power of late binding and overriding is available, but only when instance methods are used.

Oliver Charlesworth和Marko Topolnik的答案是正确的,我想进一步说明问题的部分原因:

在Java类中,成员是根据引用的类型而不是实际对象的类型来访问的。出于同样的原因,如果在类B中有一个someOtherMethodInB(),则在运行aRef = b后将无法从aRef访问它。标识符(即类,变量等名称)在编译时解析,因此编译器依赖于引用类型来执行此操作。

现在在您的示例中,在运行System.out.println(aRef.intVal);时,它会打印在A中定义的intVal的值,因为这是用于访问它的引用的类型。编译器发现aRef的类型为A,这就是它将要访问的intVal。不要忘记在B的实例中同时具有两个字段。如果您想看一下,JLS也有一个与您类似的示例" 15.11.1-1。字段访问的静态绑定"。

但是,为什么方法的行为有所不同?答案是,对于方法,Java使用后期绑定。这意味着在编译时,它将找到最合适的方法在运行时进行搜索。搜索涉及方法在某些类中被覆盖的情况。

这称为变量隐藏。当您分配aRef = b;时,aRef具有两个intVal,1只是命名为intVal,另一个隐藏在A.intVal下(请参见调试器屏幕截图),因为您的变量的类型为class A,即使您仅打印< x5> 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

d5242a71a493738d050a608277d76f01.png

我希望这可以帮助:

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!通常,如果答案包括对代码意图的解释,以及为什么不引入其他代码就能解决问题的原因,则答案会更有帮助。

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

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

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

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

谢谢回复

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

因此,子类中的变量只能视为共享相同名称的变量。

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

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

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

并且在继承中,仅方法重写,以便它只能影响其属性。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值