Java中有两种形式可以实现多态:继承和接口,本文关注继承。
继承中多态只是实例方法的多态,属性和静态方法不适用多态。或者说实例方法是动态绑定的,静态方法和成员变量是静态绑定的。
1.实例方法
classBase{voidmethod(){
System.out.println("Base method!");
}
}class Sub extendsBase{voidmethod(){
System.out.println("Sub method!");
System.out.println(var);
}}public classTest {public static voidmain(String[] args) {
Base who= newSub();
who.method();
}
}
结果
Sub method!
Java中只有private、static和final修饰的方法以及构造方法是静态绑定。
a、private方法的特点是不能被继承,也就是不存在调用其子类的对象,只能调用对象自身,因此private方法和定义该方法的类绑定在一起。
b、static方法又称类方法,类方法属于类文件。它不依赖对象而存在,在调用的时候就已经知道是哪个类的,所以是类方法是属于静态绑定。
c、final方法:final方法可以被继承,但是不能被重写,所以也就是说final方法是属于静态绑定的,因为调用的方法是一样的。
总结:如果一个方法不可被继承或者继承后不可被覆盖,那么这个方法就采用的静态绑定。
2.成员变量
classBase{
String var= "Base var";
}class Sub extendsBase{
String var= "Sub var";
}public classTest {public static voidmain(String[] args) {
Base who= newSub();
System.out.println(who.var);
}
}
结果
Base var
3.实例方法的多态是个例外。对于一个引用类型的变量,Java编译器按照它的声明的类型来处理,一个父类类型的变量引用了子类实例,该变量不会主动转为子类。
声明的类型并非实际的类型
classBase{
}class Sub extendsBase{
String subVar= "Sub var";voidsubMethod(){
System.out.println("Sub submethod!");
}
}public classTest {public static voidmain(String[] args) {
Base who= newSub();
who.subMethod(); X
who.subVar; X
}
who是Base类型,根本就没有subMethod()方法和subVar属性,直接报错。
因此,最开始提到的1.实例方法(成员方法)的多态,其前提条件就是,子类的这个方法在父类中也有,即父类方法被重写了。
因此,在 被调用方法在父类中声明,也就是说被子类覆盖的方法 的前提之下,父类变量指向子类的引用,优先调用子类(实际类型)的方法,子类找不到在父类中找。这也是多态实现的另一种说法或原因。
classBase{voidmethod(){
System.out.println("Base method!");
}
}class Sub extendsBase{
}public classTest {public static voidmain(String[] args) {
Base who= newSub();
who.method();
}
当虚拟机创建子类的时候,会创建该子类的方法列表,同时包含父类的方法列表。同时虚拟机会参数引用的实际地址,找到创建的这个子类对象,查询方法列表,如果在子类对象中找到这个方法,就直接调用。
如果没找到,就去查询父类方法,调用父类的方法。
子类继承了父类,获得了父类的属性和方法?应是,子类中有父类对象(子类的初始化会引发父类的初始化,当然这须在父类未被初始会之时),子类中super()可以构造父类,子类可以调用父类中的方法,即对方法有使用权,并非真实拥有,即所有权。