java invokespecial_java动态绑定以及invokespecial指令

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型引用,因而需要复制一份。

转载请注明出处。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值