对象方法的执行过程:
1).编译器查看对象的声明类型和方法名。假设调用x.f(param),对象x中可能会存在多个f方法,它们有不同的参数类型,比如f(int), f(String), f(double)等,编译器会列举x类中所有名为f的方法和其超类中访问属性为public且名为f的方法。
经过上述过程,编译器获得所有可能被调用的候选方法。
2).编译器查看调用方法时提供的参数类型。如果所有名为f的方法中存在一个与提供的参数类型完全匹配,就选择该方法。这个过程被称为重载解析(overloading resolution)
注:方法的签名指的是方法名字,加上方法的参数列表。
1)子类覆盖父类的方法,但该方法不能拥有比父类更严格的访问控制。特别是当父类方法是public时,子类一定要是public,否则编译器将认为该方法使用的是默认的访问控制修饰符,降低了访问控制权限,因此报错了。
2)返回类型不是方法签名的一部分,在进行方法覆盖的时候,javase5之前要求返回类型要一致。但是javase5以后允许覆盖方法的返回类型为原返回类型的子类型,称之为可协变的返回类型(covariant return type)
举个例子:
public class Demo {
public static void main(String[] args) {
A a = new B();
C c = a.display() ;
c.display() ;
}
}
class A {
public C display() {
System.out.println("this is from A!");
return new C();
}
}
class B extends A {
public D display() {
System.out.println("this is from B!");
return new D() ;
}
}
class C {
public void display() {
System.out.println("this is from C!!!");
}
}
class D extends C {
public void display() {
System.out.println("this is from D!");
}
}
public static void main(String[] args) {
A a = new B();
C c = a.display() ;
c.display() ;
}
}
class A {
public C display() {
System.out.println("this is from A!");
return new C();
}
}
class B extends A {
public D display() {
System.out.println("this is from B!");
return new D() ;
}
}
class C {
public void display() {
System.out.println("this is from C!!!");
}
}
class D extends C {
public void display() {
System.out.println("this is from D!");
}
}
程序的返回结果:
this is from B!
this is from D!
this is from D!
这其中的B和A的display方法就是具有协变的返回类型,因为B的display方法覆盖了A的display方法,然后B的display返回的结果类型是D,D是C的子类,而A的display返回结果正好是C。因此构成可协变的返回类型(covariant return type)
3)当程序运行,并且采用动态绑定调用方法时,虚拟机一定调用所引用对象的实际类型最合适的那个类的方法。
例如:C x = new D(); // D extends C
x.f(String);
x引用的实际类型是D,它是C的子类。如果D定义了方法f(String),就直接调用他,否则在其超类中寻找f(String),以此类推。
每次调用方法都要进行搜索,时间开销相当大。虚拟机的解决方法是,为每个类创建一个方法表(method table),列出所有方法的签名,和实际调用的方法。以后,每次调用一个方法,虚拟机只需要找这个表就可以了。