java中多态详解

来源:互联网 发布:一面软件 编辑:程序博客网 时间:2024/06/07 14:25

一.什么是多态?

1.多态的定义

指允许不同类的对象对同一消息做出响应。即同一消息可以根据发送对象的不同而采用多种不同的行为方式(发送消息就是函数调用)

2.多态的作用

消除类型之间的耦合关系

3.多态的说明

近代网络小说泛滥,我们可以用它来举一个例子
某日你看见你手机上有多部小说同时更新了,比如有大主宰,雪鹰领主,龙王传说…在这里我们可以描述成如下:

小说a=大主宰
小说b=雪鹰领主
小说c=龙王传说

这里所表现的就是多态,大主宰,雪鹰领主,龙王传说都是小说的子类,我们仅仅可以通过小说这个父类就能够引用不同的子类,这就是多态–我们只有在运行的时候才会知道引用变量所指向的具体实例对象

当然,这样的理解是是远远不够的,要对多态的理解入门就必须要明白是”向上转型”
在上面的例子中,小说(XS)是父类,大主宰(DZZ),雪鹰领主(XYLZ),龙王传说(LWCS)都是其子类 于是,我们定义如下代码

DZZ a=new DZZ();

对于这段代码应该都不会感觉到陌生,无非就是实例化了一个大主宰的对象 那么对于如下的这段代码呢?

XS a=new DZZ();

在这里我们这样理解,这里定义了一个XS类型的a,让它指向了DZZ对象实例。由于DZZ是继承于XS,所以DZZ可以自动向上转型为XS,所以a可以指向DZZ实例对象的。这样做存在一个非常大的好处,在继承中我们知道子类是父类的扩展,它可以提供比父类更加强大的功能,如果我们定义了一个指向子类的父类引用类型,那么它除了能够引用父类的共性外,还可以使用子类强大的功能
但是向上转型也存在一些缺憾,那就是它必定会导致一些方法和属性的丢失,而导致我们不能够获取它们。所以父类类型的运用可以调用父类中定义的所有属性和方法,对于只存在与子类中的方法和属性它就望尘莫及了

public class XS {       public void fun1() {           System.out.println("XS中的fun1");           fun2();       }    public void fun2() {         System.out.println("XS中的fun2");            }}
public class DZZ extends XS{    /*     * 子类重载父类方法     * 父类中不存在该方法,向上转型后,父类是不能引用该方法的     */      public void fun1(String a) {          System.out.println("DZZ中的fun1");          fun2();      }      /*       * 子类重写父类方法        * 调用必定使用这个方法       */      public void fun2() {          System.out.println("DZZ中的fun2");      }}
public class DuoTaiTest {     public static void main(String[] args) {         XS a=new DZZ();          a.fun1();    }}
output:XS中的fun1DZZ中的fun2

所以对于多态我们可以总结如下:
指向子类的父类引用由于向上转型了,它只能访问父类中拥有的方法和属性,而对于子类中存在而父类中不存在的方法,该引用是不能使用的,尽管是重载该方法。若子类重写了父类中的某些方法,在调用该些方法的时候,必定是使用子类中定义的这些方法(动态连接,动态调用)

对于面向对象,多态分为编译时多态和运行时多态,其中编辑时多态是静态的,主要是指方法的重载,它是根据参数列表的不同来区分不同的函数,通过编辑之后会变成两个不同的函数,在运行时谈不上多态。而运行时多态是动态的,它是通过动态绑定来实现的,也就是我们所说的多态性。

二.多态的实现

1.实现条件

在刚刚开始就剃刀了继承在为多态的实现做了准备。子类Child继承父类Father,我们可以编写一个指向子类的父类类型引用,该引用既可以处理父类Father对象,也可以处理子类Child对象,当相同的消息发送给子类或者父类对象时,该对象就会根据自己所属的引用而执行不同的行为,这就是多态。即多态性就是相同的消息使得不同的类做出不同的响应

Java实现多态有三个必要条件:继承,重写,向上转型

  • 继承:在多态中必须存在有继承关系的子类和父类
  • 重写:子类对父类中某些方法进行重新定义,在调用这些方法时就会调用子类的方法
  • 向上转型:在多态中需要将子类的引用赋给父类对象,只有这样该引用才能够具备技能调用父类的方法和子类的方法

只有满足了上述三个条件,我们才能够在同一个继承结构中使用同一的逻辑实现代码处理不同的对象,从而达到执行不同的行为
对于Java而言,它多态的实现机制遵循一个原则:当超类对象引用变量引用子类对象时,被引用对象的类型而不是引用变量的类型决定了调用谁的成员方法,但是这个被调用的方法必须是在超类中定义过的,也就是说被子类覆盖的方法

2.实现形式

  • 继承
public class XS {           private String name;        public String getName() {            return name;        }        public void setName(String name) {            this.name = name;        }        public XS() {        }        public String drink() {            return "你看的小说名字:"+getName();        }        public String toString() {              return null;        }}
public class DZZ extends XS{    public DZZ() {        setName("DZZ");    }    public String drink() {        return "你看的小说名字:"+getName();    }    public String toString() {        return "小说名:"+getName();    }}
public class XYLZ extends XS{    /**     *      */    public XYLZ() {         setName("XYLZ");    }    public String drink() {        return "你看的小说名字:"+getName();    }    public String toString() {        return "小说名:"+getName();    }}      
public class DuoTaiTest {       public static void main(String[] args) {           XS [] xs=new XS[2];           DZZ a=new DZZ();           XYLZ b=new XYLZ();           xs[0]=a;           xs[1]=b;           for(int i=0;i<2;i++) {              System.out.println(xs[i].toString()+"::::"+xs[i].drink());           }           System.out.println("-------------------");    }}
ouput:小说名:DZZ::::你看的小说名字:DZZ小说名:XYLZ::::你看的小说名字:XYLZ-------------------

在上面的代码中DZZ,XYLZ继承XS 并且重写了drink(),toString()方法,程序运行结果是调用子类中方法,输出DZZ,XYLZ的名称,这就是多态的表现。不同的对象可以执行相同的行为,但是他们都需要通过自己的实现方式来执行,这就要得益于向上转型了

我们都知道所有的类都继承自超类Object,toString()方法也是Object中方法,当我们这样写时:

Object o = new DZZ();System.out.println(o.toString());
output:小说名:DZZ

Object,XS,DZZ三者继承链关系是:DZZ—>XS—>Object。所以我们可以这样说:当子类重写父类的方法被调用时,只有对象继承链中的最末端的方法才会被调用。但是注意如果这样写:

Object o = new xs();System.out.println(o.toString());
output:null//因为DZZ并不存在于该对象继承链中

所以基于继承实现的多态可以总结如下:对于引用子类的父类类型,在处理该引用时,它适用于继承该父类的所有子类,子类对象的不同,对方法的实现也就不同,执行相同动作产生的行为也就不同。
如果父类是抽象类,那么子类必须要实现父类中所有的抽象方法,这样该父类所有的子类一定存在统一的对外接口,但其内部的具体实现可以各异。这样我们就可以使用顶层类提供的统一接口来处理该层次的方法。

  • 接口

继承是通过重写父类的同一方法的几个不同子类来体现的,那么就可就是通过实现接口并覆盖接口中同一方法的几不同的类体现的。
在接口的多态中,指向接口的引用必须是指定这实现了该接口的一个类的实例程序,在运行时,根据对象引用的实际类型来执行对应的方法。
继承都是单继承,只能为一组相关的类提供一致的服务接口。但是接口可以是多继承多实现,它能够利用一组相关或者不相关的接口进行组合与扩充,能够对外提供一致的服务接口。所以它相对于继承来说有更好的灵活性。

三.经典实例

原地址: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(new B().show(b));            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 A2--A and A3--A and D4--B and A5--B and A6--A and D7--B and B8--B and B9--A and D

其实以入门者的角度来看,前3个基本没有任何问题,但许多人从第4个开始就可能会翻水水了
从代码可以看出A,B,C,D存在如下图一样的关系
这里写图片描述
首先我们先说说,在继承链中对象方法的调用存在一个优先级:this.show(O)、super.show(O)、this.show((super)O)、super.show((super)O)。

首先我们分析一下5,a2.show(c),a2是A的引用变量,所以this就代表的是A,a2.show(c),它在A类中没有找到,于是就去找A类的超类,由于A类超类就是Object了并没有其他超类,所以我们跳到第三级,也就是this.show((super)O),C的超类有B,A,所以(super)O就为B,A,this同样是A,这里在A中找到了show(A obj),同时由于a2是B类的一个引用且B类重写了show(A obj),因此最终会调用子类B类的show(A obj)方法,结果也就是B and A。
同样我们用这个步骤来分析4,a2.show(b),a2是A的引用变量,所以this就代表的是A,a2.show(b),它在A类中没有找到,于是就去找A类的超类,由于A类超累就是Object并没有其他超类,所以我们跳到第三级,也就是this.show((super)O),B的超类有A,所以(super)O就是A,this同样是A,这里在A中找到了show(A obj),同时由于a2是B类的一个引用且B类重写了show(A obj),因此最终会调用子类B类的show(A obj)方法,结果也就是B and A。

是不是感觉非常Nice!!!!以后可以用这个方法来分析问题了

END!!!!!!

1 0
原创粉丝点击