参考博客:
【解惑】Java动态绑定机制的内幕:http://blog.csdn.net/zero_295813128/article/details/52117737
Java 虚拟机体系结构:http://hxraid.iteye.com/blog/676235
--------------------------------------------------------------------------------------------------
下面是自己的理解总结:
首先得对两个概念有理解方法区和常量池。这两个都是Java虚拟在运行时的数据区域,常量池是方法区
的一部分。
方法区
方法区与Java堆一样,是各个线程共享的内存区域,用于存储已经被虚拟机加载的类信息、常量、静态变
量、即使编译器编译后的代码等数据。
常量池
常量池是方法区的一部分,用于存放编译期所生成的各种字面量和符号引用。常量池中共有11个常量表。
常量表类型 标志值(占1 byte) 描述
CONSTANT_Utf8 1 UTF-8编码的Unicode字符串
CONSTANT_Integer 3 int类型的字面值
CONSTANT_Float 4 float类型的字面值
CONSTANT_Long 5 long类型的字面值
CONSTANT_Double 6 double类型的字面值
CONSTANT_Class 7 对一个类或接口的符号引用
CONSTANT_String 8 String类型字面值的引用
CONSTANT_Fieldref 9 对一个字段的符号引用
CONSTANT_Methodref 10 对一个类中方法的符号引用
CONSTANT_InterfaceMethodref 11 对一个接口中方法的符号引用
CONSTANT_NameAndType 12 对一个字段或方法的部分符号引用
---------------------------------------------------------------------------------------------------
对于多态的原理主要研究方法区。在Java中多态指的是它允许基类的指针或引用指向派生类的对象,而在具体访
问时实现方法的动态绑定。java中的方法调用有静态绑定和动态绑定之分,静态绑定指的是我们在编译期就已经
确定了会执行那个方法的字节码,而动态绑定只有在运行时才能知晓。
静态绑定
Java中的静态方法、私有方法以及final修饰的方法的调用,都属于静态绑定,对于重载的实例方法的
调用,也是采用静态绑定。静态绑定的原理主要是一个常量池解析的过程,下面来详解其过程:
假如有两个类A、B,在A类中我们调用了B类中的一个静态方法,在编译期,这个调用的动作会被编译成一
条静态调用指令,该指令就对应常量池中的CONSTANT_Methodref表中所存储的该方法的符号引用,通过这个符号引用
可以得到静态方法的全类名B,JVM加载B类,便会得到B类中方法的直接地址,该地址会被存储到A类常量池中对应的
常量表中,这便是常量池解析过程,再次发起调用时,就会直接根据这个直接地址调用对应方法。
以上过程可以看出,在编译阶段我们就已经确定了应该执行哪一个字节码代码。
动态绑定
动态绑定讲解之前需要了解JVM管理的一个重要的数据结构--方法表。它以数组的形式记录了当前类及其所有
超类的可见方法字节码在内存中的直接地址 。
动态绑定前面的流程与静态绑定类似,假如此处有两个类A,B继承了A类,B类中重写了A类中的f1()方法,我们采用
向上转型的方式用指向B实例的A类型引用调用f1()方法,编译器会生成一条字节码指令,该指令会去常量表中找到f1()方法信
息的符号引用,通过该引用确定调用该方法的类型全名,即A类的全名称,根据名称加载到A类的字节码,去A类所对应的方法
表中找到f1()方法,将它的直接地址记录到调用f1()方法的类的对应的常量表中,常量池解析结束,可以思考,我们此时是否能
确定调用f1()方法时执行的是哪一块的字节码,答案是不能,因为截至此时我们f1()方法指定执行的是父类中的方法,引用虽然
父类类型,但他指向的是父类对象还是子类对象是不知道的(确切地说是此时的程序不知道,程序员肯定是知道的),假如指向父类
那就是父类中的f1()方法,如果指向子类的实例,子类没有重写,依然执行父类f1()方法,如果子类重写了就应该是子类的f1()
方法。此时动态绑定就登场了,确定f1()换需要拿到B类实例在堆中的引用,通过引用找到堆中B的对象,根据对象进一步获取它的
方法表,找到方法表的f1()方法的直接地址,此时便最终确定了。