类(类的对象)在调用函数时,究竟哪一个函数被调用,是存在特定的机制的,现在我们来梳理下其步骤。
步骤一:当其调用一个函数的时候,编译器根据其声明的变量类型和调用的函数名字找到所有此“类”和其父类中具有同名的函数
Class A{public void getName(inta){
System.out.println("This is Class A and integer is " +a);
}
}
Class BextendsA{public void getName(inta){
System.out.println("This is Class B and integer is " +a);
}public void getName(doublea){
System.out.println("This is Class B and float number is " +a);
}public static voidmain(String[] args){
B b= newB();
b.getName(3.14);
A a = newB();
a.getName(3.14);
} }
首先,根据步骤一,变量b声明的类型为B,其中与b调用函数同名的函数有两个,分别为getName(int a)和 getName(double a),同时A作为B的父类,有一个访问修饰符为public,既子类可以访问且与变量b同名的函数getName(int a)。此时,经过步骤一筛选之后,变量b所调用函数的候选者有如下几个:
A:
getName(inta)
B:
getName(inta)
getName(double a)
步骤二:编译器根据传入参数类型选择最合适的函数。所以b最合适的函数为public void getName(double a)
步骤三:值得留意的是,如果此函数前有private , static ,final修饰或者方法是构造函数,根据静态绑定(static binding)原则,根据其显示声明的变量类型可以直接确定所调用的函数。与之相对的是动态绑定(dynamic binding),意思即是当程序运行时,程序会
找到变量指向的实际对象,获取其实际类型。再确定其调用的函数。
一个程序中,一般都会存在大量的函数调用,如果每一次调用都从零开始搜索,无疑会消耗大量的时间。因此,JVM会预先计算出每一个类的函数调用表(method table),如下所示
A:
getName(int ) -> A.getName(int)
B:
getName(int ) -> B.getName(int)
getName(double ) -> B.getName(int)
此表包含了每一个类可以调用的函数(无论其是自己的函数还是父类的函数,只要调用都需要纳入此表)。当JVM确定了函数实际类型时,按此表用以上步骤进行查找调用。