java静态分配和动态分配
来源:互联网 发布:淘宝店铺导航怎么删除 编辑:程序博客网 时间:2024/05/17 17:44
1.方法调用
2.分派
来看一个静态分配的例子
package com.jvm;/** * 静态分派 * @author renhj * */public class StaticDispatch {static class Human { } static class Man extends Human { } static class Women extends Human { }public void sayHello(Human guy) { System.out.println("hello, guy!"); } public void sayHello(Man guy) { System.out.println("hello, man!"); } public void sayHello(Women guy) { System.out.println("hello, women!"); } public static void main(String[] args){ Human man = new Man(); Human women = new Women(); StaticDispatch sd = new StaticDispatch(); sd.sayHello(man); sd.sayHello(women); } }
这个答案是你心目中的答案吗?
Human man = new Man(); 我们把上面代码中的“Human”称为变量的静态类型,后面的“Men”称为变量的实际类型,静态类型和实际类型在程序中都可以发生一些变化,区别是静态类型的变化仅仅在使用时发生,变量本身的静态类型不会改变,并且最终的静态类型是在编译期间可知的;而实际类型变化的结果在运行期间才可确定,编译器在编译程序时并不知道一个对象的实际类型是什么。
再回到上面例子代码中,mian()中两次调用sayHello()方法,在方法接受者已经确定是对象“src”的前提下,使用哪个重载版本,就完全取决于传入参数的数量和数据类型。编译器在重载时通过参数的静态类型而不是实际类型作为判断依据的,因此在编译阶段Java编译器根据参数的静态类型决定使用哪个重载版本。
我们使用javap命令输出这个类的字节码,输出结果字节码如下。
所有依赖静态类型来定位方法执行版本的分派动作称为静态分配,静态分配的典型动作是方法重载,静态分派发生在编译阶段,虽然编译器能确定方法的重载版本,但是很多情况下这个重载的版本并不是“唯一的”,往往只能确定一个“更加合适的”版本。产生这种模糊结论的主要原因是字面量不需要定义,所以字面量没有显示的静态类型,它的静态类型只能通过语言上的规则去理解和推断。
“更加合适”版本例子
package com.jvm;/** * 重载方法屁匹配优先级 * @author renhj * */public class Verload {private static void sayHello(char arg){System.out.println("hello char");}private static void sayHello(Object arg){System.out.println("hello Object");}private static void sayHello(int arg){System.out.println("hello int");}private static void sayHello(long arg){System.out.println("hello long");}public static void main(String[] args) {sayHello('c');}}上面代码运行后,正常回输出:hello char,如果注释掉sayHello(char arg)方法,那输出就会变成:hello int。
3.动态分配
我们接下来看一下动态分配的过程,它和多态性的另外一个重要体现--重写(Override)有着密切的关系,先看例子。
package com.jvm;/** * 动态分派 * @author renhj * */public class DynamicDispatch {static abstract class Human { protected abstract void sayHello();}static class Man extends Human { @Override protected void sayHello() { System.out.println("hello man!"); } } static class Women extends Human { @Override protected void sayHello() { System.out.println("hello women!"); } } public static void main(String[] args){ Human man = new Man(); Human women = new Women(); man.sayHello(); women.sayHello(); man = new Women(); man.sayHello(); } }运行结果:
hello man!
hello women!
hello women!
这个结果相信不会出乎任何人的意料,那Java虚拟机是如何根据实际类型来分配方法执行版本的呢?我们使用javap命令输出这个类的字节码,尝试从中寻找答案,输出结果字节码如下。
0~15主要是建立man和woman的存储空间、调用Man和Woman类型的实例构造器,并将两个实例存放在第一个和第二个局部变量表Slot之中。接下来的16~21句是关键部分,16、20两句分别是把刚刚穿件的两个对象的引用压到栈顶,17、21两句是方法调用指令,这两条调用指令从字节角度来看,无论指令(invokevirtual)还是参数完全一样,但是这两条指令最终执行的目标方法并不相同,原因需要从invokevirtual指令的多态查找过程开始说起,invokevirtual指令的运行时解析过程大致如下几个步骤:
1). 找到操作数栈顶的第一个元素所指向的对象的实际类型,记作C.
2). 如果在类型C中找到与常量池中描述符和简单名称都相符的方法,则进行访问权限的校验,如果校验不通过,则返回java.lang.IllegaAccessError异常,校验通过则直接返回方法的直接引用,查找过程结束。
3). 否则,按照继承关系从下往上一次对C的各个父类进行第二步骤的搜索和验证过程。
4). 如果始终还是没有找到合适的方法直接引用,则抛出java.lang.AbstractMethodError异常。
由于invokevirtual指令执行的第一步是在运行时确定接收者的实际类型,所以两次中的invokevirtual指令把常量池中的类方法符号引用解析到不同的直接引用上,这个就是java语言中方法重写的本质,我们把这种在运行期根据实际类型确定方法执行版本的分派过程称为动态分派。
4.虚拟机动态分派的实现
- java静态分配和动态分配
- java之动态分配和静态分配
- 静态分配和动态分配
- 动态分配和静态分配
- 动态分配和静态分配
- 深入理解Jvm--Java静态分配和动态分配完全解析
- 静态分配和动态分配内存的区别
- 静态分配内存和动态分配内存
- 静态分配和动态分配内存的区别
- ABAP字段符号静态分配和动态分配
- 内存静态分配和动态分配的区别
- 数组的静态分配和动态分配
- 静态分配和动态分配内存的区别
- 静态分配和动态分配内存的区别
- 静态分配内存和动态分配内存
- 静态分配和动态分配内存的区别
- 静态分配和动态分配内存的区别
- 静态分配和动态分配内存的区别
- Scala之Option
- DexClassLoader4.4.2动态加载分析(磁盘加载分析)
- POJ 3352Road Construction 边双联通分量
- 基于JBox2D物理引擎开发的“雷电”小游戏(三)——模拟并显示世界
- java之List接口
- java静态分配和动态分配
- 内核中的内存申请:kmalloc、vmalloc、kzalloc、get_free_pages 之间的区别
- Apache+jk+tomcat负载均衡详细配置方法
- 数据挖掘-关联规则
- HDOJ(HDU) 2106 decimal system(进制相互转换问题)
- SM2算法第五篇:socket的基本原理与实现
- Cocos2d-js 音乐or音效
- iOS自定义progressView的实现
- 九 AIDL