因此,我定义了类
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类?
#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具有封装的特性,它紧密地绑定了对象的属性和行为。因此,只有通过类引用,我们才能调用它的行为来更改其属性。在继承中,仅方法重写,因此它只能影响其属性。
评论
关于变量覆盖方面的轻微混淆的可能重复项我认为这不是一个相同的问题,因为另一种语言混合了该语言的几个功能,而这个纯粹是关于变量不是多态的
@Vic Seedoubleyew但在我看来,这个问题像是重复的
类的Hiding实例变量的可能重复