④. 动态链接(Dynamic Linking)
①. 运行时常量池位于方法区,字节码中的常量池结构如下:
②.为什么需要常量池呢?
(常量池的作用,就是为了提供一些符号和常量,便于指令的识别。下面提供一张测试类的运行时字节码文件格式)
③. 每一个栈帧内部都包含一个指向运行时常量池Constant pool或该栈帧所属方法的引用。包含这个引用的目的就是为了支持当前方法的代码能够实现动态链接。比如invokedynamic指令
④. 在Java源文件被编译成字节码文件中时,所有的变量和方法引用都作为符号引用(symbolic Refenrence)保存在class字节码文件(javap反编译查看)的常量池里。比如:描述一个方法调用了另外的其他方法时,就是通过常量池中指向方法的符号引用来表示的,那么动态链接的作用就是为了将这些符号引用(#)最终转换为调用方法的直接引用
⑤. 方法的调用:(小插曲)难点
①. 静态链接(早期绑定):当一个 字节码文件被装载进JVM内部时,如果被调用的目标方法在编译期可知,且运行期保持不变时。这种情况下将调用方法的符号引用转换为直接引用的过程称之为静态链接
(invokestatic | invokespecial)
②. 动态链接(晚期绑定):如果被调用的方法在编译期无法被确定下来,也就是说,只能够在程序运行期将调用方法的符号引用转换为直接引用,由于这种引用转换过程具备动态性,因此也就被称之为动态链接。体现了多态
(invokevirtual | invokeinterface)
③. 非虚方法: 如果方法在编译器就确定了具体的调用版本,这个版本在运行时是不可变的。这样的方法称为非虚方法
(静态方法、私有方法、final方法、实例构造器(实例已经确定,this()表示本类的构造器)、父类方法(super调用)都是非虚方法)
④. 其他所有体现多态特性的方法称为虚方法
⑤. 如下指令要重点掌握
普通调用指令: 1.invokestatic:调用静态方法,解析阶段确定唯一方法版本; 2.invokespecial:调用<init>方法、私有及父类方法,解析阶段确定唯一方法版本; 3.invokevirtual:调用所有虚方法; 4.invokeinterface:调用接口方法; 动态调用指令(Java7新增): 5.invokedynamic:动态解析出需要调用的方法,然后执行 . 前四条指令固化在虚拟机内部,方法的调用执行不可人为干预,而invokedynamic指令则支持由 用户确定方法版本。 其中invokestatic指令和invokespecial指令调用的方法称为非虚方法 其中invokevirtual(final修饰的除外,JVM会把final方法调用也归为invokevirtual指 令,但要注意final方法调用不是虚方法)、invokeinterface指令调用的方法称称为虚方法。
/** * 解析调用中非虚方法、虚方法的测试 */ class Father { public Father(){ System.out.println("Father默认构造器"); } public static void showStatic(String s){ System.out.println("Father show static"+s); } public final void showFinal(){ System.out.println("Father show final"); } public void showCommon(){ System.out.println("Father show common"); } } public class Son extends Father{ public Son(){ super(); } public Son(int age){ this(); } public static void main(String[] args) { Son son = new Son(); son.show(); } //不是重写的父类方法,因为静态方法不能被重写 public static void showStatic(String s){ System.out.println("Son show static"+s); } private void showPrivate(String s){ System.out.println("Son show private"+s); } public void show(){ //invokestatic showStatic(" 大头儿子"); //invokestatic super.showStatic(" 大头儿子"); //invokespecial showPrivate(" hello!"); //invokespecial super.showCommon(); //invokevirtual 因为此方法声明有final 不能被子类重写,所以也认为该方法是非虚方法 showFinal(); //虚方法如下 //invokevirtual showCommon();//没有显式加super,被认为是虚方法,因为子类可能重写showCommon info(); MethodInterface in = null; //invokeinterface 不确定接口实现类是哪一个 需要重写 in.methodA(); } public void info(){ } } interface MethodInterface { void methodA(); }