JAVA调用对象方法的执行过程:
①.编译器查看对象的声明类型和方法名。假设调用x.f(parameter), 且隐式参数x声明为C类型的对象,有可能在C对象中存在多个参数类型和参数个数不同的f的方法{例如:f(int)、f(int,String)和f(String)},编译器会一一列举C类中的f方法和C的超类中声明为public所有的f方法。
至此,编译器已经获得所有可能被调用的候选方法。
②.接下来,编译器将查看调用方法时所提供的参数类型和参数个数。如果所有名为f的方法中存在这样一个与提供方法的参数类型和参数个数完全匹配的方法,就选择这个方法,这个过程也称重载解析。例如:对于调用f(“This is my java”)来说,编译器会寻找f(String)这个方法。如果编译器没有找到与参数类型和参数个数匹配的方法,或者经过类型转换后有多个方法于此匹配,就会报告一个错误。
至此,已经寻找到需要调用的方法和参数类型
③.如果是此方法是被private、static、final修饰或则是构造器,那么编译器可以准确的知道应该调用哪个方法,于是便调用这个的方法。这种调用方式叫着静态绑定。静态绑定方式于此结束。
而与此对应是,调用方法依赖于隐式参数的实际类型,并且在运行时实现动态绑定,编译器采用动态绑定的方式会生成一条调用f(String)的指令(其原因请查看下面的动态绑定解析)。
④.当程序运行,并且采用动态绑定调用方法时,虚拟机一定调用与x所引用的对象的实际类型最适合的那个方法。假设x的实际类型是D,D是C的子类,如果D类定了方法f(String),编译器就直接调用它,否则就在D类的超类C中寻找f(String)方法,如果C类型中不存在这个方法,则又到寻找C的超类中寻找这个方法,以此类推。这个过程就称为动态绑定。
动态绑定解析:
每次调用方法时都要进行搜索,这样一来,时间开销相当大。因此,虚拟机预先为每一个类创建了一个方法表,其中列举出了类中的所有方法签名和实际调用的方法。而在真正调用方法的时候,虚拟机仅仅查找这个表就行了。在前面的列子中,虚拟机搜索D类的方法表。以便寻找与调用f(String)相匹配的方法。这个方法既可能是D.f(String),也可能是C.f(String),还有可能是*.f(String),这里的*是指D的超类。
----参考于《Core Java》
下面我们就举一个简单的例子:
新建一个Father类,并且让Son类继承Father基类:
package com.bin.bind;
public class TestBind {
public static void main(String[] args) {
String name = "TOM";
String hobby = "~~~sleep~~~";
int age = 19;
Father father = new Son(name, hobby, age);
String result = father.getAllInfo();
System.out.println(result);
}
}
class Father {
public Father(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public int getAge() {
return age;
}
public void setName(String name) {
this.name = name;
}
public void setAge(int age) {
this.age = age;
}
public String getAllInfo() {
return "姓名是:"+this.name+"\t年龄是:"+this.age;
}
private String name = null;
private int age = 0;
}
class Son extends Father {
public Son(String name, String hobby, int age) {
super(name, age);
setHobby(hobby);
}
public String getHobby() {
return hobby;
}
public void setHobby(String hobby) {
this.hobby = hobby;
}
public String getAllInfo() {
return "姓名是:"+super.getName()+"\t年龄是:"+
super.getName()+"\t爱好是:"+hobby;
}
private String hobby = null;
}
在Father和Son类中都存在getAllInfo()这个方法,因为getAllInfo()不是static、final、private、构造方法,所以此方法将采用动态绑定。
在程序执行的时候虚拟机会为这个两个类生成方法表
Father类:
Father(String,int)->Father(String,int)
setAge(int)->Father.setAge(int)
getAge()->Father.getAge()
setName(String)->Father.setName(String)
getName()->Father.getName()
getAllInfo()->Father.getAllInfo()
Son类:
Father(String,int)->Father(String,int)
setAge(int)->Father.setAge(int)
getAge()->Father.getAge()
setName(String)->Father.setName(String)
getName()->Father.getName()
getAllInfo()->Father.getAllInfo()
Son(String,String,age)->Son(String,String,age)
setHobby(String)->Son.setHobby(String)
getHobby()->Son.getHobby()
getAllInfo()->Son.getAllInfo()
注意:上面列举的方法并不完整,因为他们还都默认继承了一个Object超类。
下面分析这条语句:
String result = father.getAllInfo();
father.getAllInfo()的解析过程如下:
①:虚拟机提取father类的实际类型的方法表。既可能是Father类也可能是Son类。(因为这里father引用所指向的是Son类的实例对象,所以实际类型必定是Son)
②:接下来,虚拟机搜索定义getAllInfo签名的类,此时,虚拟机已经知道调用哪个方法。(Son中只有一个getAllInfo方法,且与此匹配,所以被调用) ③:虚拟机执行调用方法。(最后将返回结果赋值给result)