多态:多态通过分离做什么和怎么做,从另一角度将接口和实现分离出来,有些文章将重载视作编译时多态(其实个人感觉并没有多态的特性),将重写视为运行时多态
先来看一段代码:
class Father {
protected String type = "Father";
public Father() {
}
public void getType() {
System.out.println(type);
}
}
class Son extends Father {
protected String type = "Son";
public Son() {
}
@Override
public void getType() {
System.out.println(type);
}
}
public class TestDynamicBind {
public void getType(Father father) {
System.out.println("this is father interface");
father.getType();
}
public void getType(Son son) {
System.out.println("this is son interface");
son.getType();
}
public static void main(String[] args) {
Father son = new Son();
System.out.println(son.type); --1、Father
son.getType(); -- 2、Son
TestDynamicBind bind = new TestDynamicBind();
bind.getType(son); -- 3、this is father interface/Son
bind.getType(new Son()); --4、this is son interface/Son
}
}
程序的执行结果为:
Father
Son
this is father interface
Son
this is son interface
Son
- 1打印type属性,注意
域和静态方法是无法使用多态的,因此打印域值为Father - 2通过对象的实例方法打印type属性,虽然变量son的声明类型为Father,但实际为Son类型对象,执行实例方法时会根据
调用对象的方法表(如果对象为子类,改写了父类的方法,那么子类和父类的那些同名的方法共享一个方法表项,所有继承父类的子类的方法表中,重写的父类所定义的方法的偏移量也总是一个定值,这样 JVM 在调用实例方法时只需要指定调用方法表中的第几个方法即可)实际调用Son class的getType方法 - 3调用TestDynamicBind的getType方法,注意该类有两个getType方法(也就是包含两个重载的getType方法),运行时应该调哪一个呢,其实编译时就决定了(所以个人觉得并不具备多态的特性),因为运行时是根据参数类型决定调用方法的,
而参数类型是编译时期决定的,虽然对象实际为Son类型,后面会展示相关字节码,因此走入了getType(Father father)方法,但实际调用对象的getType方法时由于该对象为Son类型而调用了Son的getType方法,输出Son - 4直接构造了new Son()对象传入TestDynamicBind对象的getType方法,这种方式和Son son = new Son();bind.getType(son);的效果是一致的,因此走入了TestDynamicBind的getType(Son son)方法且输出Son
上字节码

- 上面是main方法的字节码,可以看到TestDynamicBind的两次getType调用(后两个标注)都是编译期决定了参数类型(LFather/LSon),实际也确定了调用哪个方法,虽然字节码命令为invokevirtual
- 对于对象实例方法的多态调用,继承父类和实现接口的实现机制虽有不同(继承的方法在父类和子类的方法表项的位置是相同的,而实现接口的方法在不同实现类的方法表项的位置很可能是不同的),但实际调用时都走了各自对象的具体实现方法
欢迎关注微信订阅号
