Java – 父类和子类拥有同名变量
请仔细观察系列两段程序的不同之处:一个Son类重写print方法,另一个则没有重写。
分析该问题必须从字节码的角度入手,否则根本不可能看清事情的本质。
成员变量(非静态的)的赋值过程:
- 默认初始化
- 显示初始化 / 代码块中初始化
- 构造器中初始化
- 有了对象之后。可以“对象.属性”或“对象.方法”的方式队成员变量进行赋值。
最重要的是要搞清楚父类和子类的同名变量是否占用一块内存!!!
package test0;
/**
* @author wsh2096
* @create 2020-10-28 8:57
*/
public class SonTest {
public static void main(String[] args) {
Father f = new Son();
System.out.println(f.x);
}
}
class Father {
int x = 10;
public Father() {
this.print();
x = 20;
}
public void print() {
System.out.println("Father.x = " + x);
}
}
class Son extends Father {
int x = 30;
public Son() {
this.print();
x = 40;
}
@Override
public void print() {
System.out.println("Son.x = " + x);
}
}
Father类的字节码:
init方法字节码:
print方法字节码
Son类的字节码:
init方法字节码:
print方法字节码
分析:
第一个输出0的原因是:在调用print方法的时候,子类Son重载该方法,所以调用子类方法进行输出,此时子类的x还没有显示初始化,所以输出的是子类x默认初始化值0。
第二个输出30的原因是:在Son类的构造器进行输出,此时Son类的x已经进行完显示初始化,将Son的x值赋值为30.
第三个输出20的原因是:此时变量f实际上指向的是Son类的实例,但是外观上是Father类,由于方法存在重载,会进行动态链接进行查找子类的具体实现方法!但是,属性十不存在重载,也就不存在动态链接,所以此时输出f的x值,是去查找Father类实例的属性x的值。(此时父类的构造器已经执行完毕,所以此时的值为20.)
对父类的print方法不进行重载!
package test0;
/**
* @author wsh2096
* @create 2020-10-28 8:57
*/
public class SonTest {
public static void main(String[] args) {
Father f = new Son();
System.out.println(f.x);
}
}
class Father {
int x = 10;
public Father() {
this.print();
x = 20;
}
public void print() {
System.out.println("Father.x = " + x);
}
}
class Son extends Father {
int x = 30;
public Son() {
this.print();
x = 40;
}
// @Override
// public void print() {
// System.out.println("Son.x = " + x);
// }
}
这个没有重载print方法的版本就不放字节码了,与重载的版本相比,只不过是少了东西,其他部分都是一样的。
最后一个输出不需要解释!
第一个输出10,是因为子类并没有重载print方法,所以调用Father的print方法,输出的就是父类的x值,此时已经完成显示初始化,为10.
第二个输出20,属性不能重载,输出Father的x值,此时完成Father构造器初始化,将值赋为20.