java中多态概念、实现原理详解
一.什么是多态?
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中的fun1 dzz中的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并不存在于该对象继承链中
所以基于继承实现的多态可以总结如下:对于引用子类的父类类型,在处理该引用时,它适用于继承该父类的所有子类,子类对象的不同,对方法的实现也就不同,执行相同动作产生的行为也就不同。
如果父类是抽象类,那么子类必须要实现父类中所有的抽象方法,这样该父类所有的子类一定存在统一的对外接口,但其内部的具体实现可以各异。这样我们就可以使用顶层类提供的统一接口来处理该层次的方法。
本篇文章的内容希望能给需要的朋友一些帮助