字段不参与多态
学习到这里的时候很好玩,记录一下。
直接来个例子,如果你能答对,也就不用看这这篇文章了。
public class FieldHasNoPolymorphic {
static class Father{
public int money = 1;
public Father(){
System.out.println("father`s constructor is invoked by:" + this);
money = 2;
showMeTheMoney();
}
public void showMeTheMoney(){
System.out.println("DynamicDispatch Result: I am Father,i have $" + money);
}
}
static class Son extends Father{
public int money = 3;
public Son(){
money = 4;
showMeTheMoney();
}
@Override
public void showMeTheMoney(){
System.out.println("DynamicDispatch Result: I am Son,i have $" + money);
}
}
public static void main(String[] args) {
Father guy = new Son();
System.out.println("Result: guy has $" + guy.money);
}
}
Father guy = new Son();
guy 的静态类型为 Father,实际类型为 Son。
1.new Son(); 对应的字节码指令:
- new :为 son 对象在堆中开辟内存空间,将对象引用压入操作数栈顶
- dup :赋值栈顶元素再次压栈
- invokespecial :调用 Son 的实例化方法( < init >() 方法),实例化 son 对象(为字段赋值 + 执行实例代码块 + 调用无参构造方法)。再传入一个 son 对象作为实例化方法中的 this 关键字
创建对象的过程这里暂不讨论。
2.进入 Son 对象实例化方法(为字段赋值 + 执行实例代码块 + 调用无参构造方法):
public Son() {
this.money = 3;
this.money = 4;
this.showMeTheMoney();
}
父类有无参构造的情况下,子类构造方法的第一行默认是 super(); 即:
public Son() {
super();
this.money = 3;
this.money = 4;
this.showMeTheMoney();
}
super() 对应的字节码指令为:
0: aload_0
1: invokespecial #1 // Method clazz/dynamicDispatch/FieldHasNoPolymorphic$Father."<init>":()V
Son 类无参构造方法的 0 号变量槽存储了 son 对象引用。通过 aload_0 指令把 son 对象压入栈顶,传入父类的无参构造方法作为方法中的 this 关键字。
3.进入父类的实例化方法(为字段赋值 + 执行实例代码块 + 调用无参构造方法):
public FieldHasNoPolymorphic$Father() {
super(); // Object <init>()
System.out.println("father`s constructor has invoked by:" + this);
this.money = 2;
this.showMeTheMoney();
this.test1();
}
-
调用 Object 类的无参构造方法
-
输出 this 关键字代表的 son 对象的引用。
fathers constructor is invoked by:clazz.dynamicDispatch.FieldHasNoPolymorphic$Son@4554617c
-
为 money 字段赋值。由于字段不参与多态,所以是给 Father 类中的 money 字段赋值为 3。
-
调用 showMeTheMoney() 方法,由于在以上代码中此方法存在多种版本,所以这个方法是虚方法,需要在运行期间动态判断此方法的版本是 Father 类的还是 Son 类的。对应的字节码指令为 invokevitrual。
执行 invokevitural 指令前,将 this 关键字对应的对象压入操作数栈顶,判断该对象的实际类型,为 Son。
执行 invokevitural 指令,调用 Son 类中的 showMeTheMoney() 方法。由于此时 Son 类还没有来得及初始化,所以输出
"DynamicDispatch Result: I am Son,i have $0"
4.父类实例化方法调用完成,返回子类实例化方法继续执行:
public Son() {
super();
this.money = 3; <——
this.money = 4;
this.showMeTheMoney();
}
为 Son 类的 money 字段赋值 4 。调用虚方法 showMeTheMoney()前进行方法分派,确定 showMeTheMoney() 为 Son 类方法。输出 "DynamicDispatch Result: I am Son,i have $4"
5.Son 类实例化方法执行完毕,返回 main 方法继续执行:
System.out.println("Result: guy has $" + guy.money);
因为字段不参与多态,所以在编译期间编译器可以通过 guy 的静态类型找到目标字段的值。而且此时 Father 类以及完成初始化,money 字段值为 2,输出 "Result: guy has $2"
end…