在Java中虚方法是指在编译阶段和类加载阶段都不能确定方法的调用入口地址,在运行阶段才能确定的方法,**即可能被重写的方法。
当我们通过“对象.方法”的形式,调用一个虚方法,我们要如何确定它具体执行哪个方法呢?
- 静态分派:先看这个对象的编译时类型,在这个对象的编译时类型中找到最匹配的方法, 最匹配的是指,实参的编译时类型与形参的类型最匹配
- 动态绑定:再看这个对象的运行时类型,如果这个对象的运行时类重写了刚刚找到的那个最匹配的方法,那么执行重写的,否则仍然执行刚才编译时类型中的那个方法
class MyClass{
public void method(Father f) {
System.out.println("father");
}
public void method(Son s) {
System.out.println("son");
}
}
class MySub extends MyClass{
public void method(Father d) {
System.out.println("sub--father");
}
public void method(Daughter d) {
System.out.println("daughter");
}
}
class Father{
}
class Son extends Father{
}
class Daughter extends Father{
}
public class TestVirtualMethod {
public static void main(String[] args) {
MyClass my = new MySub();
Father f = new Father();
Son s = new Son();
Daughter d = new Daughter();
my.method(f);//sub--
/*
(1)静态分派:看my的编译时类型MyClass,在MyClass中找最匹配的
匹配的原则:看实参的编译时类型与方法形参的类型的匹配程度
实参f的编译时类型是Father,形参(Father f) 、(Son s)
最匹配的是public void method(Father f)
(2)动态绑定:看my的运行时类型MySub,看在MySub中是否有对 public void method(Father f)进行重写
发现有重写,如果有重写,就执行重写的
public void method(Father d) {
System.out.println("sub--");
}
*/
my.method(s);//son
/*
(1)静态分派:看my的编译时类型MyClass,在MyClass中找最匹配的
匹配的原则:看实参的编译时类型与方法形参的类型的匹配程度
实参s的编译时类型是Son,形参(Father f) 、(Son s)
最匹配的是public void method(Son s)
(2)动态绑定:看my的运行时类型MySub,看在MySub中是否有对 public void method(Son s)进行重写
发现没有重写,如果没有重写,就执行刚刚父类中找到的方法
*/
my.method(d);//sub--
/*
(1)静态分派:看my的编译时类型MyClass,在MyClass中找最匹配的
匹配的原则:看实参的编译时类型与方法形参的类型的匹配程度
实参d的编译时类型是Daughter,形参(Father f) 、(Son s)
最匹配的是public void method(Father f)
(2)动态绑定:看my的运行时类型MySub,看在MySub中是否有对 public void method(Father f)进行重写
发现有重写,如果有重写,就执行重写的
public void method(Father d) {
System.out.println("sub--");
}
*/
}
}
为了加深印象我们在来两个练习:
public class Test03 {
public static void main(String[] args) {
A a1 = new A();
A a2 = new B();
B b = new B();
C c = new C();
D d = new D();
System.out.println("(1)" + a1.show(b));//A and A
/*
a1的编译时类型是A,在A类中匹配show方法,
实参b的编译时类型是B,匹配public String show(A obj)
a1的运行时类型是A,仍然执行A类中的show方法
*/
System.out.println("(2)" + a2.show(d));//A and D
/*
a2的编译时类型是A,在A类中匹配show方法,
实参d的编译时类型是D,匹配public String show(D obj)
a2的运行时类型是B,没有重写show(D obj)
*/
System.out.println("(3)" + b.show(c));//B and B
/*
b的编译时类型是B,在B类中匹配show方法,
实参c的编译时类型是C,匹配public String show(B obj)
b的运行时类型是B,没有重写show(B obj)
*/
System.out.println("(4)" + b.show(d));//A and D
/*
b的编译时类型是B,在B类中匹配show方法,
实参d的编译时类型是D,匹配 public String show(D obj)从A类继承的
b的运行时类型是B,仍然执行 public String show(D obj)
*/
}
}
class A{
public String show(D obj){
return ("A and D");
}
public String show(A obj){
return "A and A";
}
}
class B extends A{
public String show(B obj){
return "B and B";
}
public String show(A obj){
return "B and A";
}
}
class C extends B{
}
class D extends B{
}
public class Test04 {
public static void main(String[] args) {
A a1 = new A();
A a2 = new B();
B b = new B();
C c = new C();
D d = new D();
System.out.println("(1)" + a1.show(b));//A and A
/*
a1的编译时类型是A,在A类中匹配show方法,
实参b的编译时类型是B,匹配public String show(A obj)
a1的运行时类型是A,仍然执行A类中的show方法
*/
System.out.println("(2)" + a2.show(d));//B and A
/*
a2的编译时类型是A,在A类中匹配show方法,
实参d的编译时类型是D,匹配public String show(A obj)
a2的运行时类型是B,B类重写了public String show(A obj),执行重写的
*/
System.out.println("(3)" + b.show(c));//A and C
/*
b的编译时类型是B,在B类中匹配show方法,
实参c的编译时类型是C,匹配public String show(C obj)
b的运行时类型是B,没有重写show(C obj)
*/
System.out.println("(4)" + b.show(d));//B and B
/*
b的编译时类型是B,在B类中匹配show方法,
实参d的编译时类型是D,匹配 public String show(B obj)
b的运行时类型是B,仍然执行public String show(B obj)
*/
}
}
class A {
public String show(C obj) {
return ("A and C");
}
public String show(A obj) {
return "A and A";
}
}
class B extends A {
public String show(B obj) {
return "B and B";
}
public String show(A obj) {
return "B and A";
}
}
class C extends B {
}
class D extends B {
}