先上代码:
说明:
父类为Person,
子类为Man和Woman,分别重写了eat和walk方法,
并自定义了earnMoney,goShopping方法和不同的属性。
多态性的体现:当父类的引用指向子类的对象。
Person p1 = new Man();
Person p2 = new Woman();
多态的使用:
当父类引用指向子类对象,运行时调用父子类同名同参数的方法时,
实际执行的是子类重写父类的方法。(虚拟方法调用)。
p1.eat();
p1.walk();
//执行出来的是子类中的eat方法
举例理解:
老板要秘书叫个人(Person)来一起吃饭(eat方法),
叫来的人(Person)可能是男人(Man)也可能是女人(Woman),
一起吃饭时可能是霸气的吃饭(Man重写的eat方法),
也可能是优雅的吃饭(Woman重写的eat方法)。
但是,当要p1调用子类Man特有的方法earnMoney时,会报错,也就是说连编译都无法通过。
也就是说,p1只能调用Person类中声明过的方法。
总结:编译看(等号的)左边,执行看(等号的)右边。
Person p1 = new Man();
// 编译时看等号左边,类型为Person,说明在写代码时p1只能调用Person类型声明的属性和方法,不然编译不通过。
// 执行时看右边,调用的方法有以下两种情况:
// 父类有子类也有(如子类重写父类的eat方法),则执行子类重写的eat方法。
// 父类有而子类没有重写,则执行父类中的该方法。
//虽然子类没有重写,但是子类继承了父类的全部方法,等同于执行了子类中的该方法。
相当于:
编译时,编译器认为p2是Person类型,只能调用父类中声明的方法;
运行时认为p2是Man类型,实际执行的是子类继承或重写父类的方法。
这叫做虚拟方法调用。
虚拟方法调用:
正常的调用:声明什么类型,就new什么类型
虚拟方法调用:
子类中重写父类方法,在多态情况下,将此时父类中的方法称为虚拟方法。
父类根据赋给他的不同子类对象,动态调用属于子类的该方法。
这样的方法调用在编译期是无法确定的。
编译时类型和运行时类型:
Person p = new Man();
p.eat();
编译时p为Person类型,运行时p为Man类型。
方法的调用是在运行时决定的,所以调用的是Man类的eat方法。 -- 动态绑定
早绑定与晚绑定:
方法的重载:
允许存在多个同名方法,参数签名不同。编译器根据方法不同的参数表,对同名的方法做修饰。
对编译器而言,这些同名的方法在修饰后就成为不同的方法,有了不同的地址。
这些方法的调用地址在编译前就绑定了。
对于重载而言,在方法调用之前,编译器就已经确定了要调用的方法,称为早绑定或静态绑定。
而对于多态:
只有等到运行时方法调用的时候,解释运行器才会确定要调用的具体方法。称为晚绑定或动态绑定。