方法调用-动态分派

/ Jvm / 没有评论 / 322浏览

在上一篇中我们详细说明了静态分派的功能,并且演示了静态分派功能的实际应用也就是重载。下面我们继续说明一下动态分派的功能,动态分派功能的实际应用就是重写。我们和静态分派一样,首先看一下例子,然后在做详细分析。

public class DynamicDispatch {

static abstract class Human {
protected abstract void sayHello();
}

static class Man extends Human {
@Override
protected void sayHello() {
System.out.println("Man");
}
}

static class Woman extends Human {
@Override
protected void sayHello() {
System.out.println("Woman");
}
}

public static void main(String[] args) {
Human man = new Man();
Human woman = new Woman();
man.sayHello();
woman.sayHello();
man = new Woman();
man.sayHello();
}
}
Man
Woman
Woman

这个结果没有什么需要做特殊说明的,主要考察的就是方法重写的规则。下面我们具体分析一下在虚拟机执行时,是怎么判断出要执行哪个方法的呢。我们使用javap命令查看一下编译后的字节码。

C:\Java\jdk1.8.0_91\bin>javap -verbose DynamicDispatch
Classfile /C:/Java/jdk1.8.0_91/bin/DynamicDispatch.class
  Last modified 2017-4-18; size 514 bytes
  MD5 checksum 3c2556ec549314c990662ecc4f2270e1
  Compiled from "DynamicDispatch.java"
public class DynamicDispatch
  minor version: 0
  major version: 52
  flags: ACC_PUBLIC, ACC_SUPER
Constant pool:
   #1 = Methodref          #8.#22         // java/lang/Object."<init>":()V
   #2 = Class              #23            // DynamicDispatch$Man
   #3 = Methodref          #2.#22         // DynamicDispatch$Man."<init>":()V
   #4 = Class              #24            // DynamicDispatch$Woman
   #5 = Methodref          #4.#22         // DynamicDispatch$Woman."<init>":()V
   #6 = Methodref          #12.#25        // DynamicDispatch$Human.sayHello:()V
   #7 = Class              #26            // DynamicDispatch
   #8 = Class              #27            // java/lang/Object
   #9 = Utf8               Woman
  #10 = Utf8               InnerClasses
  #11 = Utf8               Man
  #12 = Class              #28            // DynamicDispatch$Human
  #13 = Utf8               Human
  #14 = Utf8               <init>
  #15 = Utf8               ()V
  #16 = Utf8               Code
  #17 = Utf8               LineNumberTable
  #18 = Utf8               main
  #19 = Utf8               ([Ljava/lang/String;)V
  #20 = Utf8               SourceFile
  #21 = Utf8               DynamicDispatch.java
  #22 = NameAndType        #14:#15        // "<init>":()V
  #23 = Utf8               DynamicDispatch$Man
  #24 = Utf8               DynamicDispatch$Woman
  #25 = NameAndType        #29:#15        // sayHello:()V
  #26 = Utf8               DynamicDispatch
  #27 = Utf8               java/lang/Object
  #28 = Utf8               DynamicDispatch$Human
  #29 = Utf8               sayHello
{
  public DynamicDispatch();
    descriptor: ()V
    flags: ACC_PUBLIC
    Code:
      stack=1, locals=1, args_size=1
         0: aload_0
         1: invokespecial #1                  // Method java/lang/Object."<init>":()V
         4: return
      LineNumberTable:
        line 1: 0

  public static void main(java.lang.String[]);     descriptor: ([Ljava/lang/String;)V     flags: ACC_PUBLIC, ACC_STATIC     Code:       stack=2, locals=3, args_size=1          0: new           #2                  // class DynamicDispatch$Man          3: dup          4: invokespecial #3                  // Method DynamicDispatch$Man."<init>":()V          7: astore_1          8: new           #4                  // class DynamicDispatch$Woman         11: dup         12: invokespecial #5                  // Method DynamicDispatch$Woman."<init>":()V         15: astore_2         16: aload_1         17: invokevirtual #6                  // Method DynamicDispatch$Human.sayHello:()V         20: aload_2         21: invokevirtual #6                  // Method DynamicDispatch$Human.sayHello:()V         24: new           #4                  // class DynamicDispatch$Woman         27: dup         28: invokespecial #5                  // Method DynamicDispatch$Woman."<init>":()V         31: astore_1         32: aload_1         33: invokevirtual #6                  // Method DynamicDispatch$Human.sayHello:()V         36: return       LineNumberTable:         line 22: 0         line 23: 8         line 24: 16         line 25: 20         line 26: 24         line 27: 32         line 28: 36 } SourceFile: "DynamicDispatch.java" InnerClasses:      static #9= #4 of #7; //Woman=class DynamicDispatch$Woman of class DynamicDispatch      static #11= #2 of #7; //Man=class DynamicDispatch$Man of class DynamicDispatch      static abstract #13= #12 of #7; //Human=class DynamicDispatch$Human of class DynamicDispatch

我们详细分析一下上述编译后的字节码。0~15行的字节码是准备动作,目的是创建man和woman的内存空间并执行Man和Woman的构造方法。也就相当于执行了源码中的:

Human man = new Man();
Human woman = new Woman();

代码。其余的字节码是执行方法调用的也就是通过调用invokevirtual指令来完成的。下面我们看一下invokevirtual指令运行时解析的过程:

动态分派:我们把这种在运行期根据实际类型确定方法执行版本的分派过程就叫动态分派。