关于java的静态绑定与动态绑定小结
一、问题
Java方法调用过程中,Jvm是如何知道调用的是哪个类的方法Jvm又是如何处理
二、概念
a、当子类和父类(接口和实现类)存在同一个方法时,子类重写父类(接口)方法时,程序在运行时调用的方法时,是调用父类(接口)的方法呢还是调用子类的方法呢我们将确定这种调用何种方法的操作称之为绑定。
?绑定又分为静态绑定和动态绑定。
静态绑定
静态绑定是在程序执行前就已经被绑定了(也就是在程序编译过程中就已经知道这个方法是哪个类中的方法)。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
publicclassStaticBindDemo {
publicstaticvoids1() {
System.out.println("static s1");
}
privatevoidp1() {
System.out.println("private p1");
}
publicfinalvoidf1() {
System.out.println("final f1");
}
}
调用方:
1
2
3
4
5
6
7
publicclassStaticCall {
publicstaticvoidmain(String[] args) {
StaticBindDemo sbd =newStaticBindDemo();
StaticBindDemo.s1();
sbd.f1();
}
}
反编译后的文件:
上面的源代码反编译后,我们可以看到
调用的是静态方法
1
8: invokestatic #4// Method com/jstar/jvm/sync/bind/StaticBindDemo.s1:()V
1、#4指的是常量沲中的第4个常量表索引项,记录的是方法s1的符号引用,jvm会根据这个符号引用找到方法f1
所在的类的全限定名: com/jstar/jvm/sync/bind/StaticBindDemo
2、紧接着JVM会加载、边接和初始化类StaticBindDemo类
3、然后在StaticBindDem类所在的方法区中找到s1()方法的直接地址,并将这个直接地址记录到StaticCall类的常量池索引为4的常量表中。这个过程叫常量池解析 ,以后再次调用StaticBindDemo.s1时,将直接找到s1方法的字节码;
4、 完成了StaticCall类常量池索引项4的常量表的解析之后,JVM就可以调用s1()方法,并开始解释执行f1()方法中的指令了。
通过上面的过程,我们发现经过常量池解析之后,JVM就能够确定要调用的s1()方法具体在内存的什么位置上了。实际上,这个信息在编译阶段就已经在StaticCall类的常量池中记录了下来。这种在编译阶段就能够确定调用哪个方法的方式,我们叫做静态绑定机制
注:Java中只有private、static和final修饰的方法以及构造方法是静态绑定。
a、private方法的特点是不能被继承,也就是不存在调用其子类的对象,只能调用对象自身,因此private方法和定义该方法的类绑定在一起。
b、static方法又称类方法,类方法属于类文件。它不依赖对象而存在,在调用的时候就已经知道是哪个类的,所以是类方法是属于静态绑定。
c、final方法:final方法可以被继承,但是不能被重写,所以也就是说final方法是属于静态绑定的,因为调用的方法是一样的。
?总结:如果一个方法不可被继承或者继承后不可被覆盖,那么这个方法就采用的静态绑定。
动态绑定
编译器在每次调用方法时都要进行搜索,时间开销相当大。因此虚拟机会预先为每个类创建一个方发表(method table),其中列出了所有方法的签名和实际调用的方法。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
publicclassFather {
publicvoidf1() {
System.out.println("father-f1");
}
publicvoidf1(inti) {
System.out.println("father-f1 params-int :"+ i);
}
}
publicclassSonextendsFather{
publicvoidf1() {
System.out.println("son f1");
}
publicvoidf1(charc) {
System.out.println("son-f1 params-c:"+ c);
}
}
publicclassDemo {
publicstaticvoidmain(String[] args) {
Father f =newSon();
f.f1();
//f.f1('c');
}
}
通过反编译Demo我们来看看jvm是怎么执行的
其中invokevirtual指令的详细调用过程是这样的:
(1) invokevirtual指令中的#4指的是Demo类的常量池中第4个常量表的索引项。这个常量表(Methodref) 记录的是方法f1信息的符号引用(包括f1所在的类名,方法名和返回类型)。JVM会首先根据这个符号引用找到调用方法f1的类的全限定名: Father。这是因为调用方法f1的类的对象father声明为Father类型。
(2) 在Father类型的方法表中查找方法f1,如果找到,则将方法f1在方法表中的索引项记录到Demo类的常量池中第4个常量表中(常量池解析)。这里有一点要注意:如果Father类型方法表中没有方法f1,那么即使Son类型中方法表有,编译的时候也通过不了。因为调用方法f1的类的对象father的声明为Father类型。
(3) 在调用invokevirtual指令前有一个aload_1指令,它会将开始创建在堆中的Son对象的引用压入操作数栈。然后invokevirtual指令会根据这个Son对象的引用首先找到堆中的Son对象,然后进一步找到Son对象所属类型的方法表.
(4) 这是通过第(2)步中解析完成的#4常量表中的方法表的索引项,可以定位到Son类型方法表中的方法f1(),然后通过直接地址找到该方法字节码所在的内存空间。
很明显,根据对象(father)的声明类型(Father)还不能够确定调用方法f1的位置,必须根据father在堆中实际创建的对象类型Son来确定f1方法所在的位置。这种在程序运行过程中,通过动态创建的对象的方法表来定位方法的方式,我们叫做动态绑定机制。
动态绑定过程:
?<1>虚拟机提取对象的实际类型的方法表。
?<2>虚拟机搜索方法签名,此时虚拟机已经知道应该调用哪种方法。(PS:方法的签名包括了:1.方法名 2.参数的数量和类型~~~~返回类型不是签名的一部分。)
?<3>虚拟机调用方法
转载自https://www.cnblogs.com/xyz-star/p/10152676.html
关于java的静态绑定与动态绑定小结 相关文章
centos7配置静态ip地址
centos7的网络IP地址配置文件在 /etc/sysconfig/network-scripts 文件夹下 进入这个文件夹 cd /etc/sysconfig/network-scripts 使用ll命令查看所有文件,找到对应的网卡,我的网卡是ifcfg-ens32 [[email protected] ~]# cd /etc/sysconfig/network-scripts[root
真正读懂jvm
一、java 文件在jvm 中的执行过程 一、装载1.将java文件编译成字节码文件,字节码文件是java 虚拟机可以识别的文件2.将java 字节码文件通过流的方式装载到内存,也就是按照虚拟机所需的格式存储在方法区中,同时在java 堆中生成一份java.lang.Class 对象,这
java-ArrayList源码
ArrayLisrt 应该不陌生,对,就是可调整大小的动态数组,具体怎么实现的呢,就让咱们一探究竟吧! 初始化: 构造方法: 构建一个初始化容量为10的空数组 //默认初始化数据容器 private static final Object[] EMPTY_ELEMENTDATA = {};/** /* 存储数组元素的
如何创建一个Java项目
目录 新建项目 项目信息配置 创建Java类 编译和运行 新建项目 首先双击eclipse进入到eclipse页面。 菜单“File”下的“New”里“Java Project”,点击即可创建Java项目。 项目信息配置 1)给项目起名称。 2)use default location(使用默认位置),当然,你
Java并发编程常识
这是why的第 85 篇原创文章 写中间件经常要做两件事: 1.延迟加载,在内存缓存已加载项。 2.统计调用次数,拦截并发量。 就这么个小功能,团队里的人十有八九写错。 上面这句话不是我说的,是梁飞在他的博客里面说的。 梁飞是谁 据网上的公开资料,梁飞,花
jsp 与jstl
区别: jsp=html+java web混合 语法较为复杂难以编写 如if语句编写如下: body%if(flag){%h2欢迎 %=name %/h2%} %/body 需要将java语句套在% %中 jstl书写较为简便if语句如下: bodyjstltest brbrc:if test="${flag== true}" 欢迎${name} c:out value="${nam
Java中的集合(最全干货精美装)
By CaesarChang 合作: [email protected] ~关注我 带你看更多精品知识 类集中最大的几个操作接口:Collection、Map、Iterator,这三个接口为以后要使用的最重点的接口。 ------------------------为了更好的学习集合 ,先简单串一下数据结构-------------
Java实现输入数字 n按顺序打印出从 1 到最大的 n 位十进制数。比如输入 3则打印出 1、2、3 一直到最大的 3 位数 999。
class Solution { public int[] printNumbers(int n) { int size=(int)(Math.pow(10,n)-1); int[] arrs=new int[size]; for(int i=0;isize;i++){ arrs[i]=i+1; } return arrs; }} 学会用 Math.pow(要被次方的数, 次方) ; 即可 他是double 一定要自己 (int 转
Java输入一个整型数组数组中的一个或连续多个整数组成一个子数组。求所有子数组的和的最大值。 要求时间复杂度为O(n)。输入: nums = [-2,1,-3,4,-1,2,1,-5,4] 输出:
By CaesarChang 合作: [email protected] ~关注我 带你看更多精品知识 见注释 简单动态规划问题 将前面的数之和做一个更新 class Solution { public int maxSubArray(int[] nums) { int Max=nums[0]; int pre=0; //记录前面的和 int cur=0; //记录当前数
Java输入整数数组 arr 找出其中最小的 k 个数。例如输入4、5、1、6、2、7、3、8这8个数字则最小的4个数字是1、2、3、4。
By CaesarChang 合作: [email protected] ~关注我 带你看更多精品知识 class Solution { public int[] getLeastNumbers(int[] arr, int k) { Arrays.sort(arr); int [] arrSort=Arrays.copyOf(arr,k); return arrSort; }} 学会用Arrays.sort([ ]) 用法