Java有三大特性:封装、继承和多态
所谓多态就是指有多个状态,比如“食物”这个概念,它可以是肉、鱼和蔬菜,因此“食物”具有多态性。在程序的运行时有多个状态,也就是父类的引用变量被哪个子类所实例化,这就是向上转型。多态实现的三个必要条件:继承、重写、向上转型。
继承:继承就是一个父类有多个子类,比如一个Food类可以有Meet类和Fish类。
重写:重写很关键,就是子类中必须重写父类的方法,这样在向上转型后的调用中才能调用不同子类中的方法。
向上转型:父类的引用变量被子类所实例化,如Food f = new Meet。向上转型会造成一些方法和属性的丢失,他可以调用所有父类中的的属性和方法,但不能调用只存在于子类中的类型和方法。
【1】多态的使用规则
【2】基于继承的多态
【3】基于接口的多态
【4】多态的扩展
多态的使用规则
子类的父类引用向上转型,只能访问父类的属性和方法,不能访问只存在于子类的方法,重载也不可以,必须重写才可以调用子类中方法和属性,(重载是返回相同,参数个数不同。重写是返回和参数都相同)也就是子类中重写了父类中的方法时,调用时肯定调用子类中的方法。
基于继承的多态
基于继承的多态表现为一个或者多个子类对父类方法的重写,子类对象向上转型之后可以调用子类中这些重写的方法。下面是基于继承的例子:
package TwoWeek; public class Extend1{ public static void main(String args[]){ Food f1 = new Food(); f1.Eat(); Food f2 = new Meet(); f2.Eat(); Food f3 = new Fish(); f3.Eat(); //f3.HaveBone();// 使用Food中没有的方法会出错 } } class Food{ Food(){ System.out.println("这是Food的构造方法"); } public void Eat(){ System.out.println("吃食物"); } } class Meet extends Food{ public void Eat(){ System.out.println("我们逮肉"); } } class Fish extends Food{ public void Eat(){ System.out.println("我们吃鱼"); } public void HaveBone(){ System.out.println("鱼是有刺的"); } }
输出结果为:
这是Food的构造方法 吃食物 这是Food的构造方法 我们逮肉 这是Food的构造方法 我们吃鱼在向上转型的之后,调用各自子类中的Eat()方法。
基于接口的多态
接口的多态和集成的多态是相似的,用几个不同的类来实现接口并覆盖接口中的同一方法,继承只有单继承,而接口不同,可以实现多继承,所以其有更高的灵活性。
package TwoWeek; public class Extend2{ public static void main(String args[]){ Meet1 f2 = new Meet1(); f2.Eat(); Fish1 f3 = new Fish1(); f3.Eat(); f3.HaveBone();// 我们发现这个是可以不会报错的 } } interface Food1{ public void Eat(); } class Meet1 implements Food1{ public void Eat(){ System.out.println("我们逮肉"); } } class Fish1 implements Food1{ public void Eat(){ System.out.println("我们吃鱼"); } public void HaveBone(){ System.out.println("鱼是有刺的"); } }
输出结果为:
<span style="font-size:18px;">这是Food的构造方法 吃食物 这是Food的构造方法 我们逮肉 这是Food的构造方法 我们吃鱼</span><span style="color:#3333ff;font-size:24px; font-weight: bold;"> </span>
当父类对象的引用变量引用子类对象时,被应用对象而不是引用变量决定了调用谁的成员方法,但是这边被调用的方法是在子类中定义过的,也就是被子类重写的方法,根据调用链中调用方法的优先级来确认方法。下面是引用自大神博客http://blog.csdn.net/chenssy/article/details/12786385的经典继承例子:
public class A { public String show(D obj) { return ("A and D"); } public String show(A obj) { return ("A and A"); } } public class B extends A{ public String show(B obj){ return ("B and B"); } public String show(A obj){ return ("B and A"); } } public class C extends B{ } public class D extends B{ } public class Test { 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)); System.out.println("2--" + a1.show(c)); System.out.println("3--" + a1.show(d)); System.out.println("4--" + a2.show(b)); System.out.println("5--" + a2.show(c)); System.out.println("6--" + a2.show(d)); System.out.println("7--" + b.show(b)); System.out.println("8--" + b.show(c)); System.out.println("9--" + b.show(d)); } }输出结果为:
1--A and A 2--A and A 3--A and D 4--B and A 5--B and A 6--A and D 7--B and B 8--B and B 9--A and D确实是从第四个开始就迷糊了,下面来捋一捋。
下面我们来分析第四个a2.show(b),其中a2是A类型的引用变量,也就是父类中的引用变量,引用的是B类型的对象,也就是应该调用b类中的show(B obj)方法,出现"B and B"的结果,当不能忘了一件事,该show()方法必须在类中也存在且被子类覆写才可以,但是父类中没有存在。那就只能按照继承链总的的调用优先级来执行了,优先级是这样的(this.show(O)、super.show(O)、this.show((super)O)、super.show((super)O))所有才会在A类中找到show(A obj),又因为B类中覆写了show(A obj),所以最终调用B类中的方法。
在分析一下第五个,便于加深理解,a2.show(c),a2是A类型,所以this = A,因为引用的也是B类型对象,所以首先也在B里边找,没有找到。然后到this也就是A里边找,发现又没有找到,然后按照规定就到A的super类里边找,但是A没有超类,那就按照第三继承规则this.show((super)O),C的超类为A,且this是A,所以a.show(a),有因为B中覆写了它,所以结果还是B and A.