JVM方法调用过程

  重载和重写

  同一个类中,如果出现多个名称相同,并且参数类型相同的方法,将无法通过编译.因此,想要在同一个类中定义名字相同的方法,那么它们的参数类型必须不同.这种方法上的联系就是重载.

  重载的方法在编译过程中即可完成识别.具体到每一个方法调用,Java编译器会根据所传入参数的声明类型(有别实际类型)来选取重载方法.

  选取过程如下:

    1.不考虑对基本类型自动装拆箱(auto-boxing,auto-unboxing),以及可变长参数的情况下选取重载方法;

    2.如果1中未找到适配的方法,则允许自动装拆箱,但不允许可变长参数的情况下选取重载方法;

    3.如果2中未找到适配的方法,则在允许自动装拆箱以及可变长参数的情况下选取重载方法.

  JVM的静态绑定和动态绑定

  Java虚拟机识别方法的关键在于类名/方法名/方法描述符(method descriptor).注:方法描述符由方法的参数类型/返回类型构成.

  Java虚拟机中的静态绑定(static binding)指的是在解析时便能够直接识别目标方法的情况;而动态绑定(dynamic binding)则指的是需要在运行过程中根据调用者的动态类型来识别目标方法的情况.

  具体来说,Java字节码中与调用相关的指令共有五种:

    1.invokestatic:用于调用静态方法

    2.invokespecial:用于调用私有实例方法/构造器,以及使用super关键字调用父类的实例方法/构造器,和所有实现接口的默认方法

    3.invokevirtual:用于调用非私有实例方法

    4.invokeinterface:用于调用接口方法

    5.invokedynamic:用于调用动态方法

  示例代码如下:

interface 客户 {
  boolean isVIP();
}

class 商户 {
  public double 折后价格 (double 原价, 客户 某客户) {
    return 原价 * 0.8d;
  }
}

class 奸商 extends 商户 {
  @Override
  public double 折后价格 (double 原价, 客户 某客户) {
    if (某客户.isVIP()) {                         // invokeinterface      
      return 原价 * 价格歧视 ();                    // invokestatic
    } else {
      return super. 折后价格 (原价, 某客户);          // invokespecial
    }
  }
  public static double 价格歧视 () {
    // 咱们的杀熟算法太粗暴了,应该将客户城市作为随机数生成器的种子。
    return new Random()                          // invokespecial
           .nextDouble()                         // invokevirtual
           + 0.8d;
  }
}

  调用指令的符号引用

  在编译过程中,目标方法的具体内存地址尚未确定.这时,Java编译器会暂时用符号引用来表示该目标方法.这一符号引用包括目标方法所在的类或接口的名字,以及目标方法的方法名和方法描述符.

  符号引用存储在class文件的常量池中.根据目标方法是否为接口方法,又可分为接口符号引用和非接口符号引用.

  对于非接口符号引用,假定该符号引用所指向的类为 C,则 Java 虚拟机会按照如下步骤进行查找。

    1.在 C 中查找符合名字及描述符的方法。

    2.如果没有找到,在 C 的父类中继续搜索,直至 Object 类。

    3.如果没有找到,在 C 所直接实现或间接实现的接口中搜索,这一步搜索得到的目标方法必须是非私有、非静态的。并且,如果目标方法在间接实现的接口中,则需满足 C 与该接口之间没有其他符合条件的目标方法。如果有多个符合条件的目标方法,则任意返回其中一个。

  从这个解析算法可以看出,静态方法也可以通过子类来调用。此外,子类的静态方法会隐藏(注意与重写区分)父类中的同名、同描述符的静态方法。

  对于接口符号引用,假定该符号引用所指向的接口为 I,则 Java 虚拟机会按照如下步骤进行查找。

    1.在 I 中查找符合名字及描述符的方法。

    2.如果没有找到,在 Object 类中的公有实例方法中搜索。

    3.如果没有找到,则在 I 的超接口中搜索。这一步的搜索结果的要求与非接口符号引用步骤 3 的要求一致。

  经过上述的解析步骤之后,符号引用会被解析成实际引用。对于可以静态绑定的方法调用而言,实际引用是一个指向方法的指针。对于需要动态绑定的方法调用而言,实际引用则是一个方法表的索引。

猜你喜欢

转载自www.cnblogs.com/nyatom/p/9379013.html