java多态,是指方法可以重载、子类重写父类的protected以及public方法。这些全都离不开一个一个叫动态绑定的东西。
动态绑定只是jvm在运行时会根据运行时的数据去执行相关操作,确切的说调用对象的方法或者对对象赋值的时候,jvm会根据实际对象来执行这个操作。
静态绑定静态方法不存在动态绑定,指令使用的是invokestatic,并且解析常亮池的方法引用的时候是根据引用类型去解析的,如果是Father型引用,就是Father的setName,如果是Son型引用就是Son的setName
这里做研究的是Father 、Son;Son 继承Father,其中有两个属性,一个是实例属性 age,一个是类属性 name。两者分别用父类指向子类的方式去执行,
结果会是咋样?呵呵,当然结果在注释中已经说出来了
代码如下:
public class Father
{
public Long age;
public static String name;
public static void main(String[] args){
Father fa=new Father();
fa.age=10L;
Father fa2=new Son();
fa2.age=30L;
fa.setName("son");
fa2.setName("father");
System.out.println(Father.name);//father
System.out.println(Son.name);//null
System.out.println(fa.name);//father
System.out.println(fa2.name);//father
System.out.println(fa.age);//10L
System.out.println(fa2.age);//30L
}
public static void setName(String name){
Father.name=name;
}
}
class Son extends Father{
public Long age;
public static String name;
public static void setName(String name){
Son.name=name;
}
}
那是什么原因呢?
我们从jvm的指令执行的角度去定位。
用
javap -p -v -version -l -c -s -sysinfo Father.class 去显示jvm执行的指令,其中关键部分如下:
这是创建Father对象,并对Father对象的age赋值。putfield是对实例的赋值,在putfield之前执行了aload_1这条,意思就是将需要赋值的实例对象放入栈里,给putfield 使用,而这个对象就是Father的实例对象,存在局部变量表 index=1的位置
0: new #2 // class Father
3: dup
4: invokespecial #3 // Method "":()V
7: astore_1
8: aload_1
9: ldc2_w #4 // long 10l
12: invokestatic #6 // Method java/lang/Long.valueOf:(J)Ljava/lang/Long;
15: putfield #7 // Field age:Ljava/lang/Long;
这是创建son对象,并对son对象的age赋值;其中这里就用到了动态绑定,虽然是Father类型的引用但aload_2 是index=2位置的实例。也即是Son实例化的对象,实际上是对Son的age赋值。
18: new #8 // class Son
21: dup
22: invokespecial #9 // Method Son."":()V
25: astore_2
26: aload_2
27: ldc2_w #10 // long 30l
30: invokestatic #6 // Method java/lang/Long.valueOf:(J)Ljava/lang/Long;
33: putfield #7 // Field age:Ljava/lang/Long;
而这里是虽然aload_1 但又pop了,而且invokestatic指令和当前实例无关,最后调用的就是Father的setName。
这是 fa.setName("son");这句的执行
36: aload_1
37: pop
38: ldc #12 // String son
40: invokestatic #13 // Method setName:(Ljava/lang/String;)V
这是fa2.setName("father");这句的执行;而这里是虽然aload_2 也pop了,而且invokestatic指令和当前实例无关,最后调用的就是Father的setName
43: aload_2
44: pop
45: ldc #14 // String father
47: invokestatic #13 // Method setName:(Ljava/lang/String;)V
下面介绍一个invokespecial指令,注意,黑色粗体的“操作栈”是指jvm执行该指令时,当前的jvm操作栈状态,objectref 指当前指令所属的操作实例
invokespecial 指令详解
操作:
调用实例方法;专门为父类,私有方法,以及实例方法处理
格式:
invokespecial
indexbyte1
indexbyte2
编号
invokespecial = 183 (0xb7)
操作栈
..., objectref, [arg1, [arg2 ...]] → //非实例化方法的时候
... //调实例化方法的时候,操作栈就是这种情况
使用例子:
18: new #8 // class Son
21: dup
22: invokespecial #9 // Method Son."":()V
25: astore_2
26: aload_2
这里为什么要dup呢,dup是将栈顶数值复制一份并送入至栈顶。因为invokespecial会消耗掉一个Son型引用,因而需要复制一份。
转载请注明出处。