码迷,mamicode.com
首页 > 其他好文 > 详细

JVM学习笔记(二)--方法调用之静态分配和动态分配

时间:2018-04-16 22:45:38      阅读:186      评论:0      收藏:0      [点我收藏+]

标签:style   sys   定义   jvm   line   运行   学习笔记   .com   img   

本篇文章从JVM的角度来理解Java学习中经常提到的重载和重写。

方法调用:方法调用不等同于方法执行,在Java虚拟机中,方法调用仅仅是为了确定调用哪个版本的方法。方法调用分为解析调用和分派。解析调用一定是静态的,而分派可以是静态的,也可以是动态的。我们这里只介绍分派中的静态分配和动态分配。

  • 静态分配所有依赖静态类型来定位方法执行版本的分派动作称为静态分配。

下面看个例子,顺便来猜一下结果(面试中经常遇到):

 

 1 class Human {
 2     
 3 }
 4 
 5 class Man extends Human{
 6     
 7 }
 8 
 9 class Woman extends Human {
10     
11 }
12 
13 public class overLoadTest {
14     
15     public void sayHello(Human guy){
16         System.out.println("Hello guy!");
17     }
18     public void sayHello(Man man){
19         System.out.println("Hello man!");
20     }
21     public void sayHello(Woman woman){
22         System.out.println("Hello woman!");
23     }
24     
25 
26     public static void main(String[] args){
27         Human man = new Man();
28         Human woman = new Woman();
29         
30         overLoadTest sr = new overLoadTest();
31         
32         sr.sayHello(man);
33         sr.sayHello(woman);
34         
35     }
36 }

 

运行结果如下:

技术分享图片

 

如果不明白为什么是这样的结果,我们先来了解一下,什么是静态类型和实际类型。

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

这里Human为静态类型,man和woman为实际类型。区别为静态类型的变化仅仅在使用时发生,变量本身的静态类型不会改变,并且最终的静态类型是在编译期间可知的;而实际类型变化的结果在运行期间才可确定,编译器在编译程序时并不知道一个对象的实际类型是什么。


静态分配最典型的应用就是方法重载。我们再来复习一下方法重载的定义:在一个类中有多个方法,名称相同但参数列表不同(参数个数,参数类型,参数顺序)。上面例子就是方法重载。这里的运行结果毫不意外。


sr.sayHello(man);
sr.sayHello(woman);

man和woman的静态类型都是Human。

  • 动态分配 :所有依赖实际类型来定位方法执行版本的分派动作称为静态分配。

动态分配的典型应用为重写。下面为重写的例子

 

 1 abstract class Human_Test {
 2     public abstract void sayHello();
 3 }
 4 
 5 class Man_Test extends Human_Test{
 6      @Override  
 7     public void sayHello() {
 8         System.out.println("Hello man!");
 9     }
10     
11 }
12 
13 class Woman_Test extends Human_Test {
14      @Override  
15     public void sayHello() {
16         System.out.println("Hello woman!");
17     }
18     
19 }
20 
21 public class OverrideTest {
22     public static void main(String[] args){
23         Human_Test man = new Man_Test();
24         Human_Test woman = new Woman_Test();
25         
26         man.sayHello();  
27         woman.sayHello();  
28               
29         man = new Woman_Test();  
30         man.sayHello(); 
31     }
32     
33     
34     
35 }

 

 

 

 运行结果:

技术分享图片

 

因为动态分配是根据实际类型来确定要调用的方法的,这样的结果也比较符合我们的思维习惯。那么JVM又是怎么根据实际类型来找到目标方法的呢?用Javap来分析一下字节码:

 技术分享图片

看上图17和21行,可以看到已经找到了Human中的sayHello(),但是invokevirtual指令是多态查找指令。其过程如下:

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

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

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

4). 如果始终还是没有找到合适的方法直接引用,则抛出java.lang.AbstractMethodError异常。

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

JVM学习笔记(二)--方法调用之静态分配和动态分配

标签:style   sys   定义   jvm   line   运行   学习笔记   .com   img   

原文地址:https://www.cnblogs.com/yiRain1992/p/8858543.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!