在Java中重写成员变量(变量隐藏)
我正在研究JAVA中重写成员函数,并考虑尝试重写成员变量 所以,我定义了类在Java中重写成员变量(变量隐藏),java,overriding,Java,Overriding,我正在研究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 identify
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类?变量在Java中不是多态的;它们不会相互重写。当您在子类中创建同名变量时,这称为隐藏。生成的子类现在实际上将同时具有两个属性。您可以使用
super.var
或((超类)this.var
从超类访问该超类。变量甚至不必是同一类型的;它们只是两个共享名称的变量,很像两个重载方法。变量在编译时解析,方法在运行时解析。aRef的类型为A,因此aRef.Intvalue在编译时解析为1。根据Java规范,扩展时,实例变量不会被子类从超类重写
因此,子类中的变量只能被视为具有相同名称的变量
另外,当在创建B实例的过程中调用A的构造函数时,变量(intVal)被初始化,因此输出。好吧,我希望你得到答案。如果没有,您可以尝试在调试模式下查看。子类B可以访问两个intVal。它们不是多态的,因此不会被覆盖
如果你使用B的参考,你会得到B的intVal。如果您使用A的引用,您将获得A的intVal。就这么简单。Java具有封装特性,这意味着它紧密地绑定了对象的属性和行为。因此,只有通过类引用,我们才能调用它的行为来更改它的属性 在继承中,只有方法重写,所以它只能影响它的属性 来自JLS Java SE 7版本§15.11.1: 由于缺少对字段访问的动态查找,程序可以通过简单的实现高效运行。后期绑定和重写的功能是可用的,但只有在使用实例方法时才可用 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使用后期绑定。这意味着在编译时,它会在运行时查找最合适的方法。搜索涉及在某些类中重写方法的情况。Java中的字段没有多态性。
变量
决策在编译时发生,因此总是基类变量(不是子类的继承变量)将被访问
因此,无论何时发生向上投射,请始终记住
1) 将访问基类变量
2) 将调用子类方法(如果发生重写,则调用重写方法,否则将调用继承自父类的方法)。重写Java中的概念
Parent parent=new Parent();
parent.behaviour();
函数将根据对象类型进行重写,变量将根据引用类型进行访问
Parent parent=new Child();
parent.behaviour();
Parent parent=new Child();
System.out.println(parent.state);
这里的parent
是父类的引用,但保存了子类的对象,所以在这种情况下将调用子类函数
Child child=new Child();
child.behaviour();
这里child
持有child类的对象,因此将调用child类函数
Parent parent=new Parent();
parent.behaviour();
此处parent
保存父类的对象,因此将调用父类函数
Parent parent=new Parent();
parent.behaviour();
Parent parent=new Child();
parent.behaviour();
Parent parent=new Child();
System.out.println(parent.state);
引用类型是父类,因此访问的是父类变量,而不是子类变量
Child child=new Child();
System.out.println(child.state);
Parent parent=new Parent();
System.out.println(parent.state);
这里的引用类型是Child,因此访问的是Child类变量,而不是父类变量
Child child=new Child();
System.out.println(child.state);
Parent parent=new Parent();
System.out.println(parent.state);
这里的引用类型是Parent,因此可以访问Parent类变量。我希望这有助于:
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");
}
}
所以重写基类的变量是不可能的,但基类变量值可以从继承类的构造函数中设置(更改)。这称为变量隐藏。分配
aRef=b时
,aRef
有两个intVal,一个命名为justintVal
,另一个隐藏在A.intVal
下(请参见调试器屏幕截图),因为您的变量类型为class A
,即使您只打印intVal
java int