1. 多态
对象可以有多种形态,可以存在不同的形式。但多态是基于继承关系(父类和子类),也可以是实现关系(接口和实现类)。
是将子类对象赋给父类变量,或用接口变量接收实现类对象,在运行时期表现出具体的子类或实现类特征。
2. 作用
当把不同的子类对象当作父类类型来看待,可以屏蔽不同子类对象之间的差异,从而写出通用的代码达到通用编程,以适应需 求的不断变化。
3. 多态的常见情况
SuperClass obj = new SubClass(); //SuperClass为SubClass的父类
3.1 若某方法function()仅存在于父类中,不存在于子类中,调用obj.function(),则编译通过,调用的是父类的function()
3.2 若方法function()存在于子类,不存在于父类中,调用obj.function(),则编译报错
3.3 若方法function()存在于父类,也存在于子类中(即子类对该方法进行了复写),调用obj.function(),则编译通过,调用的是 子类中的function()
3.4 若方法function()存在于父类,也存在于子类,但均为static方法,调用obj.function(),则编译通过,调用的是父类的 function()
4. 字段不存在多态特征
在父类中声明某个可继承的成员变量,仅仅表示,子类具有访问的权限,但访问的都是同一块内存空间。若子类也声明了与父 类同名的成员变量,则在初始化的过程中,子类开辟了一个新的内存空间,用于存放自己的成员变量
5. 多态的原理
5.1 绑定: 将一个方法调用同一个方法主体关联起来称为绑定。绑定分为前期绑定和后期绑定两种方式。
5.2 前期绑定:在程序执行之前进行绑定(如果有的话,由编译器和连接程序实现)
5.3 后期绑定:在运行时根据对象的实际类型进行绑定,也称为动态绑定和运行时绑定。
java中除了static方法和final方法,private方法之外,都是后期绑定。这也就解释了3中的四种类型。如果方法不是static或者final(private)的,那么绑定是后期绑定,即在编译时(在编译期,只是确保调用方法的存在,并对调用参数和返回值执行类型检查),方法调用和方法体没有关联起来,只有到了运行时,根据引用所指向的对象类型,将方法调用和方法体关联起来。
class Fish {
public static void play() { //static方法
System.out.println("Fish.paly()");
}
final void eat() { //final方法
System.out.println("Fish.eat()");
}
private void swim() { //private方法
System.out.println("Fish.swim()");
}
}
class GoldenFish extends Fish{
public static void play() {
System.out.println("GoldenFish.play()");
}
/*void eat() {
System.out.println("GoldenFish.eat()"); --编译报错,不允许复写父类final方法
}*/
public void swim() {
System.out.println("GoldenFish.swim()");
}
}
public class Cooker {
@Test
public void test01() {
Fish fish = new GoldenFish();
fish.play(); --Fish.play()
fish.eat(); --Fish.eat()
//fish.swim(); --编译报错
}
}
可参考:https://blog.youkuaiyun.com/zhongyili_sohu/article/details/8165043
6. 成员变量也不存在多态现象
首先看实例:
public class Super {
public int field = 0;
public int getField() {
return field;
}
}
public class Sub extends Super{
public int field = 1;
public int getField() {
return field;
}
public int getSuperField(){
return super.field;
}
}
public class FieldAccess {
public static void main(String[] args) {
Super sup = new Sub();
System.out.println("super.field=" + sup.field); //该处访问的是父类的field字段 0
System.out.println("super.genField()=" + sup.getField()); 1
Sub sub = new Sub();
System.out.println("sub.filed=" + sub.field); 1
System.out.println("sub.getField()=" + sub.getField()); 1
System.out.println("sub.getSuperField()=" + sub.getSuperField()); 0
}
}
解析:
当Sub对象向上转型为Super引用时,任何成员变量操作都由编译器解析,类似于前期绑定,不存在多态现象。
在本例中,为Super.field和Sub.field分配了不同的存储空间。这样,sup实际上包含两个叫field的成员变量:它自己的和从父类继承的。如果声明的类型用的时Super类型,则访问的是父类super的field字段。