欢迎访问 生活随笔!

生活随笔

当前位置: 首页 > 编程语言 > java >内容正文

java

java 静态分派_Java中的静态分派与动态分派

发布时间:2025/3/19 java 26 豆豆
生活随笔 收集整理的这篇文章主要介绍了 java 静态分派_Java中的静态分派与动态分派 小编觉得挺不错的,现在分享给大家,帮大家做个参考.

本文是《深入理解Java虚拟机》8.3.2节的读书笔记,理解有误的地方,欢迎指正

首先是两个概念:

静态类型,即是变量声明时的类型。

实际类型,变量实例化时采用的类型。

比如我们有这样一段代码

class Human {}

public class Man extends Human {

public static void main(String[] args) {

Human man = new Man();

}

}

我们就称变量 man 的静态类型为 Human,实际类型为 Man。

静态分派

所有依赖静态类型来定位方法执行版本的分派动作称为静态分派,其典型应用是方法重载(根据参数的静态类型来定位目标方法)。

静态分派发生在编译阶段,因此确定静态分派的动作实际上不是由虚拟机执行的。

动态分派

在运行期根据实际类型确定方法执行版本。

比如这样一段代码,其中man和woman的静态类型都是Human,但是实际类型各异:

public class DynamicDispatch {

public static void main(String[] args) {

Human man = new Man();

Human woman = new Woman();

man.sayHello(); // Output : man say hello

woman.sayHello(); // Output : woman say hello

}

private static abstract class Human {

protected abstract void sayHello();

}

private static class Man extends Human {

protected void sayHello() {

System.out.println("man say hello");

}

}

private static class Woman extends Human {

protected void sayHello() {

System.out.println("woman say hello");

}

}

}

编译过后所得的字节码文件如下:

(常量池的部分内容)

Constant pool:

#1 = Methodref #8.#23 // java/lang/Object."":()V

#2 = Class #24 // DynamicDispatch$Man

#3 = Methodref #2.#25 // DynamicDispatch$Man."":(LDynamicDispatch$1;)V

#4 = Class #26 // DynamicDispatch$Woman

#5 = Methodref #4.#25 // DynamicDispatch$Woman."":(LDynamicDispatch$1;)V

#6 = Methodref #13.#27 // DynamicDispatch$Human.sayHello:()V

public class DynamicDispatch {

public DynamicDispatch();

Code:

0: aload_0

1: invokespecial #1 // Method java/lang/Object."":()V

4: return

public static void main(java.lang.String[]);

Code:

0: new #2 // class DynamicDispatch$Man

3: dup

4: aconst_null

5: invokespecial #3 // Method DynamicDispatch$Man."":(LDynamicDispatch$1;)V

8: astore_1

9: new #4 // class DynamicDispatch$Woman

12: dup

13: aconst_null

14: invokespecial #5 // Method DynamicDispatch$Woman."":(LDynamicDispatch$1;)V

17: astore_2

18: aload_1 // 将第二个引用类型本地变量(man)推送至操作数栈栈顶

19: invokevirtual #6 // Method DynamicDispatch$Human.sayHello:()V,调用#6代表的实例方法,并且方法的接收者就是操作数栈顶元素

22: aload_2

23: invokevirtual #6 // Method DynamicDispatch$Human.sayHello:()V

26: return

}

在main函数中,0~8是创建Man对象并赋到man,9~17是创建woman对象并赋到woman,18就是将man对象压入操作数栈栈顶,接下来19调用Human的sayHello方法,执行的字节码指令是invokevirtual,其具有多态查找过程,在运行时的解析过程大致为:

找到操作数栈顶的第一个元素所指向的对象的实际类型,记为C。

如果在类型C中找到与常量中的描述符合简单名称都相符的方法,则进行访问权限校验,如果通过则返回这个方法的直接引用,查找过程结束;如果权限校验不通过,返回java.lang.IllegalAccessError异常。

否则,按照继承关系从下往上一次对C的各个父类进行第2步的搜索和验证过程。

如果始终没有找到合适的方法,则抛出 java.lang.AbstractMethodError异常。

这里方法的接收者——即操作数栈栈顶元素是man对象,最后的结果就是调用了Man中的sayHello方法。同样的,对woman对象的方法调用也是如此,最后执行的是Woman中的sayHello方法。

由于invokevirtual指令执行的第一步就是在运行期间确定接收者的实际类型,所以两次调用中的invokevirtual指令把常量池中的类方法符号引用解析到了不同的直接引用上,这个过程就是Java方法重写的本质。

总结

以上是生活随笔为你收集整理的java 静态分派_Java中的静态分派与动态分派的全部内容,希望文章能够帮你解决所遇到的问题。

如果觉得生活随笔网站内容还不错,欢迎将生活随笔推荐给好友。