多态的定义多态的三个必要条件多态的表现形式多态的种类多态的实现方式基于继承实现的多态基于接口实现的多态经典案例答案分析多层父子调用的优先级
写的有点饶,但是可以看到几个重点是引用的变量和该变量的方法在没运行时我们是不确定它的具体类型的。
那么为什么会有这种情况呢,根据在继承中的提现我们发现会发生这种情况的就是向下转型的问题。 即;我们可以确定猫是动物,但是反过来如果别人给你是一只被袋子装起来的动物(向上转型)你是无法判断它到底是猫还是小老虎(向下转型)不是么。
因此多态出现的情况就很明显了,它必须有一个前提场景才能发生;
实现多态有三个必要条件:继承(extent)、重写(overriding)、向上转型
通过实例说明
public class Animal { public void fun1(){ System.out.PRintln("Animal 的Fun....."); fun2(); } public void fun2(){ System.out.println("Animal 的Fun2..."); }}public class Cat extends Animal{ /** * @desc 子类重载父类方法 * 父类中不存在该方法,向上转型后,父类是不能引用该方法的 * @param a * @return void */ public void fun1(String a){ System.out.println("Cat 的 Fun1..."); fun2(); } /** * 子类重写父类方法 * 指向子类的父类引用调用fun2时,必定是调用该方法 */ public void fun2(){ System.out.println("Cat 的Fun2..."); } public static void main(String[] args) { Animal a = new Cat(); a.fun1(); }}Output
Animal 的Fun.....Cat 的Fun2...从程序的运行结果中我们发现, a.fun1()首先是运行父类Animal中的fun1().然后再运行子类Cat中的fun2()。
所以对于多态我们可以总结如下:
指向子类的父类引用由于向上转型了,它只能访问父类中拥有的方法和属性,而对于子类中存在而父类中不存在的方法,该引用是不能使用的,尽管是重载该方法。若子类重写了父类中的某些方法,在调用这些方法的时候,必定是使用子类中定义的这些方法(动态连接、动态调用)。
java中的多态的表现形式一般分为两种:overriding(重写)和overloading(重载)
重写overriding是父类和子类之间多态性的一种表现重载overloading是一个类中多态性的表现。如果在子类中定义某方法与其父类有相同的名称和参数,我们就可以这样说此方法被子类重写overriding,子类中的对象使用这个方法的时候,将调用子类中中的定义,此时对子类来说,父类中的方法定义如同被”屏蔽”了一样(a不能调用fun1(String str)方法)。如果在一个类中定义了多个同名的方法,它们或有不同的参数或有不同的参数类型,则称为方法的重载overloading。并且overloading的方式是可以改变返回值类型的,但是父类中并没有这些重载的方法。
两种实现方式: - 基于继承 - 基于接口
上面的例子都是基于继承实现的,当然下面这个也是基于继承:
public class Wine { private String name; public String getName() { return name; } public void setName(String name) { this.name = name; } public Wine(){ } public String drink(){ return "喝的是 " + getName(); } /** * 重写toString() */ public String toString(){ return null; }}public class JNC extends Wine{ public JNC(){ setName("JNC"); } /** * 重写父类方法,实现多态 */ public String drink(){ return "喝的是 " + getName(); } /** * 重写toString() */ public String toString(){ return "Wine : " + getName(); }}public class JGJ extends Wine{ public JGJ(){ setName("JGJ"); } /** * 重写父类方法,实现多态 */ public String drink(){ return "喝的是 " + getName(); } /** * 重写toString() */ public String toString(){ return "Wine : " + getName(); }}public class Test { public static void main(String[] args) { //定义父类数组 Wine[] wines = new Wine[2]; //定义两个子类 JNC jnc = new JNC(); JGJ jgj = new JGJ(); //父类引用子类对象 wines[0] = jnc; wines[1] = jgj; for(int i = 0 ; i < 2 ; i++){ System.out.println(wines[i].toString() + "--" + wines[i].drink()); } System.out.println("-------------------------------"); }}OUTPUT:
Wine : JNC--喝的是 JNCWine : JGJ--喝的是 JGJ-------------------------------在上面的代码中JNC、JGJ继承Wine,并且重写了drink()、toString()方法,程序运行结果是调用子类中方法,输出JNC、JGJ的名称,这就是多态的表现。不同的对象可以执行相同的行为,但是他们都需要通过自己的实现方式来执行,这就要得益于向上转型了。
我们都知道所有的类都继承自超类Object,toString()方法也是Object中方法,当我们这样写时:Object o = new JGJ(); System.out.println(o.toString());所以基于继承实现的多态可以总结如下:对于引用子类的父类类型,在处理该引用时,它适用于继承该父类的所有子类,子类对象的不同,对方法的实现也就不同,执行相同动作产生的行为也就不同。
这里就不写代码了,接口继承,执行的方法根据具体的子类型来确定,借口本身是不能直接调用的,所以执行的具体方法根据传递的子类型来执行它的实现方法。
这是一个非常变态的多态案例,作者写得非常详细:
http://blog.csdn.net/thinkGhoster/archive/2008/04/19/2307001.aspx
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)); }}当超类对象引用变量引用子类对象时,被引用对象的类型而不是引用变量的类型决定了调用谁的成员方法,但是这个被调用的方法必须是在超类中定义过的,也就是说被子类覆盖的方法。 (但是如果强制把超类转换成子类的话,就可以调用子类中新添加而超类没有的方法了。)
好了,先温习到这里,言归正传!上面的案例实际上涉及方法调用的优先问题
优先级由高到低依次为:this.show(O)、super.show(O)、this.show((super)O)、super.show((super)O)。让我们来看看它是怎么工作的。
比如④,a2.show(b),a2是一个引用变量,类型为A,则this为a2,b是B的一个实例,于是它到类A里面找show(B obj)方法,没有找到,于是到A的super(超类)找,而A没有超类,因此转到第三优先级this.show((super)O),this仍然是a2,这里O为B,(super)O即(super)B即A,因此它到类A里面找show(A obj)的方法,类A有这个方法,但是由于a2引用的是类B的一个对象,B覆盖了A的show(A obj)方法,因此最终锁定到类B的show(A obj),输出为"B and A”。新闻热点
疑难解答