在JVM中,将符号引用转换为调用方法的直接引用与方法的绑定机制相关。
一 链接
1 静态链接
当一个字节码文件被装载进 JVM 内部时,如果被调用的目标方法在编译期可知,且运行期保持不变时,这种情况下将调用方法的符号引用转换为直接引用的过程称之为静态链接。
2 动态链接
如果被调用的方法在编译期无法被确定下来,也就是说,只能够在程序运行期将调用的方法的符号转换为直接引用,由于这种引用转换过程具备动态性,因此也被称之为动态链接。
二 绑定机制
对应的方法的绑定机制为:早期绑定(Early Binding)和晚期绑定(Late Binding)。绑定是一个字段、方法或者类在符号引用被替换为直接引用的过程,这仅仅发生一次。
1 早期绑定
早期绑定就是指被调用的目标方法如果在编译期可知,且运行期保持不变时,即可将这个方法与所属的类型进行绑定,这样一来,由于明确了被调用的目标方法究竟是哪一个,因此也就可以使用静态链接的方式将符号引用转换为直接引用。
2 晚期绑定
如果被调用的方法在编译期无法被确定下来,只能够在程序运行期根据实际的类型绑定相关的方法,这种绑定方式也就被称之为晚期绑定。
三 早晚期绑定的发展历史
随着高级语言的横空出世,类似于Java一样的基于面向对象的编程语言如今越来越多,尽管这类编程语言在语法风格上存在一定的差别,但是它们彼此之间始终保持着一个共性,那就是都支持封装、继承和多态等面向对象特性,既然这一类的编程语言具备多态特悄,那么自然也就具备早期绑定和晚期绑定两种绑定方式。
Java中任何一个普通的方法其实都具备虚函数的特征,它们相当于C++语言中的虚函数(C++中则需要使用关键字virtual来显式定义)。如果在Java程序中不希望某个方法拥有虚函数的特征时,则可以使用关键字final来标记这个方法。
四 实战
1 代码
/**
* 说明早期绑定和晚期绑定的例子
*/
class Animal {
public void eat() {
System.out.println("动物进食");
}
}
interface Huntable {
void hunt();
}
class Dog extends Animal implements Huntable {
@Override
public void eat() {
System.out.println("狗吃骨头");
}
@Override
public void hunt() {
System.out.println("捕食耗子,多管闲事");
}
}
class Cat extends Animal implements Huntable {
public Cat() {
super(); // 表现为:早期绑定
}
public Cat(String name) {
this(); // 表现为:早期绑定
}
@Override
public void eat() {
super.eat(); // 表现为:早期绑定
System.out.println("猫吃鱼");
}
@Override
public void hunt() {
System.out.println("捕食耗子,天经地义");
}
}
public class AnimalTest {
public void showAnimal(Animal animal) {
animal.eat(); // 表现为:晚期绑定
}
public void showHunt(Huntable h) {
h.hunt(); // 表现为:晚期绑定
}
}
2 晚期绑定
调用父类方法
public void showAnimal(Animal animal) {
animal.eat(); // 表现为:晚期绑定
}
对应字节码
0 aload_1
1 invokevirtual #2 <com/atguigu/java2/Animal.eat>
4 return
invokevirtual:调用实例方法
aload_1:将第二个引用类型本地变量推送至栈顶
调用接口方法
public void showHunt(Huntable h) {
h.hunt(); // 表现为:晚期绑定
}
对应字节码
0 aload_1
1 invokeinterface #3 <com/atguigu/java2/Huntable.hunt> count 1
6 return
invokeinterface:调用接口方法
3 早期绑定
invokespecial:只能在三种情况下使用:
• the instance initialization method, <init>
• a private method of this
• a method in a superclass of this。