java中的静态分派和动态分派

1.理解前提

在理解静态分派和动态分派之前我们需要先理解静态类型和动态类型。

静态类型,是在编译期间可知的,什么意思呢,就是在使用javac命令编译java文件时确定的指令集。

动态类型,则是编译期间不可知,只有在运行期间才能确定的。

2.静态分派

为了更好的说明静态分派,参考如下重载方法的代码

public class Person {

    public void sayHello() {
        System.out.println("hello person");
    }

    public void sayHello(String hello) {
        System.out.println(hello);
    }

    public void sayGoodBye() {
        System.out.println("goodbye person");
    }

    public static void main(String[] args) {
        Person person = new Person();
        person.sayHello();
        person.sayHello("hello again Person");
    }
}

然后使用javac 命令反编译Person.class文件得到如下

javac -c -v Person.class
Classfile /E:/Pro_bak/QGZWXX/QGZWXX/WebContent/js/sys/Person.class
  Last modified 2019-6-20; size 909 bytes
  MD5 checksum 71d7cd35bec0c71e958b464fb68b1722
  Compiled from "Person.java"
public class array.Person
  minor version: 0
  major version: 52
  flags: ACC_PUBLIC, ACC_SUPER
Constant pool:
   #1 = Methodref          #11.#31        // java/lang/Object."<init>":()V
   #2 = Fieldref           #32.#33        // java/lang/System.out:Ljava/io/PrintStream;
   #3 = String             #34            // hello person
   #4 = Methodref          #35.#36        // java/io/PrintStream.println:(Ljava/lang/String;)V
   #5 = String             #37            // goodbye person
   #6 = Class              #38            // array/Person
   #7 = Methodref          #6.#31         // array/Person."<init>":()V
   #8 = Methodref          #6.#39         // array/Person.sayHello:()V
   #9 = String             #40            // hello again Person
  #10 = Methodref          #6.#41         // array/Person.sayHello:(Ljava/lang/String;)V
  #11 = Class              #42            // java/lang/Object
  #12 = Utf8               <init>
  #13 = Utf8               ()V
  #14 = Utf8               Code
  #15 = Utf8               LineNumberTable
  #16 = Utf8               LocalVariableTable
  #17 = Utf8               this
  #18 = Utf8               Larray/Person;
  #19 = Utf8               sayHello
  #20 = Utf8               (Ljava/lang/String;)V
  #21 = Utf8               hello
  #22 = Utf8               Ljava/lang/String;
  #23 = Utf8               sayGoodBye
  #24 = Utf8               main
  #25 = Utf8               ([Ljava/lang/String;)V
  #26 = Utf8               args
  #27 = Utf8               [Ljava/lang/String;
  #28 = Utf8               person
  #29 = Utf8               SourceFile
  #30 = Utf8               Person.java
  #31 = NameAndType        #12:#13        // "<init>":()V
  #32 = Class              #43            // java/lang/System
  #33 = NameAndType        #44:#45        // out:Ljava/io/PrintStream;
  #34 = Utf8               hello person
  #35 = Class              #46            // java/io/PrintStream
  #36 = NameAndType        #47:#20        // println:(Ljava/lang/String;)V
  #37 = Utf8               goodbye person
  #38 = Utf8               array/Person
  #39 = NameAndType        #19:#13        // sayHello:()V
  #40 = Utf8               hello again Person
  #41 = NameAndType        #19:#20        // sayHello:(Ljava/lang/String;)V
  #42 = Utf8               java/lang/Object
  #43 = Utf8               java/lang/System
  #44 = Utf8               out
  #45 = Utf8               Ljava/io/PrintStream;
  #46 = Utf8               java/io/PrintStream
  #47 = Utf8               println
{
  public array.Person();
    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 3: 0
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0       5     0  this   Larray/Person;

  public void sayHello();
    descriptor: ()V
    flags: ACC_PUBLIC
    Code:
      stack=2, locals=1, args_size=1
         0: getstatic     #2                  // Field java/lang/System.out:Ljava/io/PrintStream;
         3: ldc           #3                  // String hello person
         5: invokevirtual #4                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
         8: return
      LineNumberTable:
        line 6: 0
        line 7: 8
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0       9     0  this   Larray/Person;

  public void sayHello(java.lang.String);
    descriptor: (Ljava/lang/String;)V
    flags: ACC_PUBLIC
    Code:
      stack=2, locals=2, args_size=2
         0: getstatic     #2                  // Field java/lang/System.out:Ljava/io/PrintStream;
         3: aload_1
         4: invokevirtual #4                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
         7: return
      LineNumberTable:
        line 10: 0
        line 11: 7
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0       8     0  this   Larray/Person;
            0       8     1 hello   Ljava/lang/String;

  public void sayGoodBye();
    descriptor: ()V
    flags: ACC_PUBLIC
    Code:
      stack=2, locals=1, args_size=1
         0: getstatic     #2                  // Field java/lang/System.out:Ljava/io/PrintStream;
         3: ldc           #5                  // String goodbye person
         5: invokevirtual #4                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
         8: return
      LineNumberTable:
        line 14: 0
        line 15: 8
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0       9     0  this   Larray/Person;

  public static void main(java.lang.String[]);
    descriptor: ([Ljava/lang/String;)V
    flags: ACC_PUBLIC, ACC_STATIC
    Code:
      stack=2, locals=2, args_size=1
         0: new           #6                  // class array/Person
         3: dup
         4: invokespecial #7                  // Method "<init>":()V
         7: astore_1
         8: aload_1
         9: invokevirtual #8                  // Method sayHello:()V
        12: aload_1
        13: ldc           #9                  // String hello again Person
        15: invokevirtual #10                 // Method sayHello:(Ljava/lang/String;)V
        18: return
      LineNumberTable:
        line 18: 0
        line 19: 8
        line 20: 12
        line 21: 18
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0      19     0  args   [Ljava/lang/String;
            8      11     1 person   Larray/Person;
}
SourceFile: "Person.java"

我们重点关注反编译文件中的图示内容

 在java代码里面分别调用了sayHello()和sayHello(String hello)两个重载的方法。

通过图中红框我们可以看出,在编译文件中已经分别确定了调用的方法,没有混淆,而这个过程并没有经过虚拟机运行,所以这个确认重载方法调用的过程在编译期间就完成了,这个过程就是静态分派。而两个方法中的参数就可以成为是静态类型。总而言之,根据静态类型定位方法执行版本的分派动作成为静态分派。

3.动态分派

为了更好的说明,我们使用重写,代码如下,两个类,Person和Man

public class Person {

    public void sayHello() {
        System.out.println("hello person");
    }

    public void sayHello(String hello) {
        System.out.println(hello);
    }

    public void sayGoodBye() {
        System.out.println("goodbye person");
    }

    public static void main(String[] args) {
        Person person = new Person();
        person.sayHello();
        person.sayHello("hello again Person");
    }
}


public class Man extends Person {
    @Override
    public void sayGoodBye() {
        System.out.println("goodbye man");
    }

    public static void main(String[] args) {
        Person person = new Person();
        person.sayGoodBye();
        person = new Man();
        person.sayGoodBye();
    }
}

如代码所示重写了sayGoodBye(),这个时候我们反编译Man.class

javac -c -v Man.class

结果如下

Classfile /E:/Pro_bak/QGZWXX/QGZWXX/WebContent/js/sys/Man.class
  Last modified 2019-6-20; size 681 bytes
  MD5 checksum dc174ca15248b325b11b4e27edbdff77
  Compiled from "Man.java"
public class array.Man extends array.Person
  minor version: 0
  major version: 52
  flags: ACC_PUBLIC, ACC_SUPER
Constant pool:
   #1 = Methodref          #5.#25         // array/Person."<init>":()V
   #2 = Fieldref           #26.#27        // java/lang/System.out:Ljava/io/PrintStream;
   #3 = String             #28            // goodbye man
   #4 = Methodref          #29.#30        // java/io/PrintStream.println:(Ljava/lang/String;)V
   #5 = Class              #31            // array/Person
   #6 = Methodref          #5.#32         // array/Person.sayGoodBye:()V
   #7 = Class              #33            // array/Man
   #8 = Methodref          #7.#25         // array/Man."<init>":()V
   #9 = Utf8               <init>
  #10 = Utf8               ()V
  #11 = Utf8               Code
  #12 = Utf8               LineNumberTable
  #13 = Utf8               LocalVariableTable
  #14 = Utf8               this
  #15 = Utf8               Larray/Man;
  #16 = Utf8               sayGoodBye
  #17 = Utf8               main
  #18 = Utf8               ([Ljava/lang/String;)V
  #19 = Utf8               args
  #20 = Utf8               [Ljava/lang/String;
  #21 = Utf8               person
  #22 = Utf8               Larray/Person;
  #23 = Utf8               SourceFile
  #24 = Utf8               Man.java
  #25 = NameAndType        #9:#10         // "<init>":()V
  #26 = Class              #34            // java/lang/System
  #27 = NameAndType        #35:#36        // out:Ljava/io/PrintStream;
  #28 = Utf8               goodbye man
  #29 = Class              #37            // java/io/PrintStream
  #30 = NameAndType        #38:#39        // println:(Ljava/lang/String;)V
  #31 = Utf8               array/Person
  #32 = NameAndType        #16:#10        // sayGoodBye:()V
  #33 = Utf8               array/Man
  #34 = Utf8               java/lang/System
  #35 = Utf8               out
  #36 = Utf8               Ljava/io/PrintStream;
  #37 = Utf8               java/io/PrintStream
  #38 = Utf8               println
  #39 = Utf8               (Ljava/lang/String;)V
{
  public array.Man();
    descriptor: ()V
    flags: ACC_PUBLIC
    Code:
      stack=1, locals=1, args_size=1
         0: aload_0
         1: invokespecial #1                  // Method array/Person."<init>":()V
         4: return
      LineNumberTable:
        line 3: 0
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0       5     0  this   Larray/Man;

  public void sayGoodBye();
    descriptor: ()V
    flags: ACC_PUBLIC
    Code:
      stack=2, locals=1, args_size=1
         0: getstatic     #2                  // Field java/lang/System.out:Ljava/io/PrintStream;
         3: ldc           #3                  // String goodbye man
         5: invokevirtual #4                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
         8: return
      LineNumberTable:
        line 6: 0
        line 7: 8
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0       9     0  this   Larray/Man;

  public static void main(java.lang.String[]);
    descriptor: ([Ljava/lang/String;)V
    flags: ACC_PUBLIC, ACC_STATIC
    Code:
      stack=2, locals=2, args_size=1
         0: new           #5                  // class array/Person
         3: dup
         4: invokespecial #1                  // Method array/Person."<init>":()V
         7: astore_1
         8: aload_1
         9: invokevirtual #6                  // Method array/Person.sayGoodBye:()V
        12: new           #7                  // class array/Man
        15: dup
        16: invokespecial #8                  // Method "<init>":()V
        19: astore_1
        20: aload_1
        21: invokevirtual #6                  // Method array/Person.sayGoodBye:()V
        24: return
      LineNumberTable:
        line 10: 0
        line 11: 8
        line 12: 12
        line 13: 20
        line 14: 24
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0      25     0  args   [Ljava/lang/String;
            8      17     1 person   Larray/Person;
}
SourceFile: "Man.java"

重点关注图示部分

 图中红框标记显示,两个调用都是Person中的sayGoodBye()方法,但是我们都知道,第二个应该是调用Man中重写的sayGoodBye(),所以编译期间具体调用的对象是不能确定的,要到虚拟机中运行的时候才能确定,总而言之,在虚拟机运行期间确定分派的过程称为动态分派。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值