09. Java基础之多态

什么叫多态?从字面上理解就是多种形态,即对同一个客体,可以有多种不同的形式。就好像糖一样,有多种口味,你想吃什么口味的就可以吃什么口味。但在程序中,却不是你想要怎样就怎样。更多的是需要怎样去做就怎样去做。来一个算是比较官方的解释:在面向对象语言中,接口的多种不同的实现方式即为多态。引用Charlie Calverts对多态的描述——多态性是允许你将父对象设置成为一个或更多的他的子对象相等的技术,赋值之后,父对象就可以根据当前赋值给它的子对象的特性以不同的方式运作(摘自“Delphi4 编程技术内幕”)。简单的说,就是一句话:允许将子类类型的指针赋值给父类类型的指针。多态性在Object Pascal和C++中都是通过虚函数实现的,而在java中,多态的实现主要通过方法重载和方法重写来实现的。实际上, 我们在前面说继承一章的时候,就已经使用过多态这个特性了,只是说还没有将其表现出来而已。请看我们之前定义的几个类: 
 

你看,这是我们之前设立的两个类,看起来就是一个继承关系而已是吧。实际上,如果我们做以下操作,或许你就发现里面大有乾坤:

操作:新建一个Test类,实现以下代码: 

如果单纯看右面的代码,我们会觉得说这就是一个普通的继承嘛是吧,但若仔细观察里面的代码(注释处),你会发现,在这个Test类中,执行了这么一个操作:

Animal giraffe =new Giraffe();

而这个操作我们之前真的是前所未见呀,见过最相近的应该也是这个:

Giraffe giraffe =new Giraffe();

那么,这两者有什么区别呢?其实,尽管这两行代码最后所得到的结果是一样的,但是两者却发生了本质的不同。为什么呢?因为前者就是我们要介绍的多态,而后者则是简单的创建一个Giraffe对象的过程而已。嗯,一头雾水,为什么一个Animal类型的变量能够引用Giraffe类型的对象呢?这是因为,Giraffe继承了Animal的一些特性,当我们使用Giraffe的对象时,实际上也可以理解为使用的是Animal对象,但不同的是,我们能使用的Animal对象仅限于Animal仅有的,如果超出了Animal的部分,才当做时Giraffe的自带属性,而如果我们用一个父类型去引用子类对象时,会先访问到子类中重写的父类方法(父类的方法不会再执行),如果子类没有重写父类的方法,才会执行父类中的重写办法。同时,子类中没有继承到父类的部分,是不能被执行的。什么意思呢?代码解释: 
修改子类相关方法:不重写Animal类中的eat方法,增加Giraffe的run方法: 

重新执行Test,结果如下: 

发现了没?我们在新修改的Giraffe中并没有重写eat方法,所以它执行的是Animal中的方法,而对于已经重写的sleep方法,则执行了子类重写的部分。那么当我们打算执行run方法呢?结果如下: 

你看出现了错误,意思为run方法没有在Animal类中声明。 
所以,这就是我们为什么说:如果我们用一个父类型去引用子类对象时,会先访问到子类中重写的父类方法(父类的方法不会再执行),如果子类没有重写父类的方法,才会执行父类中的重写办法。同时,子类中没有继承到父类的部分,是不能被执行的。而这种调用方式,就是多态的一种状态,叫做向上转型,也是最为容易理解的一种多态方式。 
那么,既然有向上转型,自然也有向下转型啦。那么有同学就说了。向上转型是子类转父类,那么向下当然就是父类转子类了,于是巴拉巴拉,把代码修改为:

Giraffe giraffe =(Giraffe) new Animal();

你们觉得这样理解对吗?我们事实说话:执行代码: 

我们发现,不行,出现了ClassCastException异常,解释是说Animal类不能转化为Giraffe类。说明这个思路是错误的。那么,什么样才是正确的呢?就是:已经转为父类型的子类对象,再转回成子类型,才叫做向下转型:WTF??我要先向上转型为父类,再从父类向下转型为子类???那不是直接和调用子类对象是一样的嘛!看到这里,相信这是大多数人的心声。但实际上,这两者是有区别的,什么区别呢?就是我们可以在向上转型的过程中,对即将进行的向下转型操作进行一定的修改。什么意思呢?请看例子:我们对Animal类和Giraffe进行一定的修改,新增一个属性count,表示吃了多少顿饭。代码如下: 
Animal类:

package ClassTest;
//Animal类
public class Animal {
    //新增一个count属性。并设置set和get方法
    private int count;
    public int getCount() {
        return count;
    }
    public void setCount(int count) {
        this.count = count;
    }
    void eat(){
        System.out.println("动物吃了饭");
    }
    void sleep(){
        System.out.println("动物睡觉");
    }
}

接下来是Giraffe类:

package ClassTest;
//继承自Animal类的Giraffe类
public class Giraffe extends Animal {
    //新增一个count属性
    private int count;
    public int getCount() {
        return count;
    }
    public void setCount(int count) {
        this.count = count;
    }
    //重写的构造方法
    public Giraffe(int count) {
        super();
        this.count = count;
    }
    //重写的sleep方法
    void sleep(){
        System.out.println("长颈鹿吃了"+count+"顿草");
    }
     //子类增加的方法
    void run(){
        System.out.println("长颈鹿四条腿狂跑");
    }

}

我们先进行第一个Test,代码如下: 

发现和使用

Giraffe giraffe = new Giraffe(1); 

并没有什么不同,不用怕,接下来就有改变了:

在Test类中新增代码: 

你看,如此对giraffe的count属性进行修改后,是不是也影响到了giraffe2的count属性呢?这里其实就是借助先向上,再先下这个空隙来实现对数据的修改。当然这只是向下转型的一个方法,实际上向下转型更多是应用在java中的设计模式及泛型之中。而这些知识点会在以后的学习中帮助你更容易去理解多态的作用。 
好了,接下来,我们对多态进行一个总结: 
1、不管是向上转型,还是向下转型,我们涉及到的都是子类和父类的问题,也就是说多态必须存在继承关系

2、正如上面所说,多态就是多种形态。也就是说,当我们需要实现多态的时候,就需要有父类的方法被子类重写。否则,如果没有重写的方法,就看不出多态的特性,一切按照父类的方法来,还不如不要继承,直接在父类中添加相应的方法,然后在实例化好了

3、不管是向上转型还是向下转型,都有一个共性,就是父类指向子类对象。如果没有这个,也就没有了多态。

而关于多态的表现形式,分为两类: 
静态分派,即方法的重载。表现为方法同名不同参,可以存在多个同名的方法,只要参数不一致就行,我们可以根据实际情况调用其中一个,所以称为静态分派。 
动态分派,即方法的重写。表现为同名同参不同的执行操作。只能出现一个方法,而方法内部的操作时动态更改的。每次能且仅能调用一个方法。因为它并没有被人选择地特性,所以为动态。

转自https://blog.csdn.net/dengminghli/article/details/54809876

原文地址:https://www.cnblogs.com/Hermioner/p/9575245.html

时间: 2024-08-28 02:58:59

09. Java基础之多态的相关文章

Java基础十一--多态

Java基础十一--多态 一.多态定义 简单说:就是一个对象对应着不同类型. 多态在代码中的体现: 父类或者接口的引用指向其子类的对象. 1 /* 2 3 对象的多态性. 4 5 class 动物 6 {} 7 8 class 猫 extends 动物 9 {} 10 11 class 狗 extends 动物 12 {} 13 14 15 16 猫 x = new 猫(); 17 18 动物 x = new 猫();//一个对象,两种形态. 19 20 21 22 猫这类事物即具备者猫的形态,

【Java基础】多态

首先先来个总结: 什么是多态 面向对象的三大特性:封装.继承.多态.从一定角度来看,封装和继承几乎都是为多态而准备的.这是我们最后一个概念,也是最重要的知识点. 多态的定义:指允许不同类的对象对同一消息做出响应.即同一消息可以根据发送对象的不同而采用多种不同的行为方式.(发送消息就是函数调用) 实现多态的技术称为:动态绑定(dynamic binding),是指在执行期间判断所引用对象的实际类型,根据其实际的类型调用其相应的方法. 多态的作用:消除类型之间的耦合关系. 现实中,关于多态的例子不胜

黑马程序员-Java基础-面向对象—多态、Object类、内部类、异常

第一讲  多态 1.  定义 可以理解为事物存在的多种体现形态. 在java中,当函数的功能内容不确定时,可以将此方法定义为抽象方法,让其子类去实现.当子类对象不同时,调用同一个函数名,会执行不同的函数体,得到不同的结果,从而体现了多态性. 2.  多态的体现形式 父类的引用指向了自己的子类对象:Fu f = new Zi(): 父类的引用也可以接受自己的子类对象: 3.  多态的前提条件 必须是类与类之间有关系,要么继承,要么实现(接口): 函数之间存在覆盖(重写): 4.  多态的好处 大大

Java基础:多态(重载和重写)

(1)域与静态方法 记住"只有普通方法的调用是多态的". 而域和静态方法不是:对于域的访问,在编译期间就已经进行解析和绑定了.而如果某个方法是静态的,就不具备多态性. (2)编写构造器有一条准则: * 用尽可能简单的方法使得对象进入正常状态,尽量避免调用其他方法. * 构造器内能够安全调用的方法只有那些final方法(private默认final),因为他们无法被覆盖. (3)多态概念: 多态是指一个程序中同名的不同方法共存的情况. 这些方法同名的原因是它们的最终功能和目的都相同,但是

黑马程序员-Java基础之多态

面向对象之多态   多 态(面向对象特征之一):函数本身就具备多态性,某一种事物有不同的具体的体现. 体现:父类引用或者接口的引用指向了自己的子类对象.//Animal a = new Cat(); 多态的好处:提高了程序的扩展性. 多态的弊端:当父类引用指向子类对象时,虽然提高了扩展性,但是只能访问父类中具备的方法,不可以访问子类中特有的方法.(前期不能使用后期产生的功能,即访问的局限性) 多态的前提: 1:必须要有关系,比如继承.或者实现.     2:通常会有覆盖操作.   多态的出现思想

java基础之 多态

在面向对象编程(Object-Oriented Programming, OOP)中,多态机制无疑是其最具特色的功能,甚至可以说,不运用多态的编程不能称之为OOP.这也是为什么有人说,使用面向对象语言的编程和面向对象的编程是两码事. 多态并没有一个严格的定义,维基百科上给它下的定义比较宽松: Subtype polymorphism, almost universally called just polymorphism in the context of object-oriented pro

java基础之多态的那些事儿

同一个对象在不同时刻体现出来的不同状态,叫多态. 多态的前提: a.有继承或者实现关系. b.有方法重写. c.有父类或者父接口引用指向子类对象. 多态的分类: a:具体类多态 class Fu{ } class Zi extends Fu{ } Fu f = new Zi();     b.抽象类多态 abstract class Fu{ } class Zi extends Fu{ } Fu f = new Zi(): c.接口多态 interface Fu{ } class Zi impl

2.25 Java基础总结 ①多态②向上向下转型③instanceof

①多态②向上向下转型③instanceof 一.多态即相同行为,不同实现有两种方法:重载和重写分类:①静态多态:编译时已经确定效果,所用重载实现(不是指static)②动态多态:编译未知,运行已知(使用动态绑定和重写实现) 动态绑定:父类的引用指向子类的对象,执行相应的子类方法,而不是父类的,从而实现多态性 二.向上转型,向下转型向上转型:一个父类的引用可以指向不同的子类对象,或者说一个子类的对象可以被当做一个父类类型低精度向高精度转pet = cat;自动转,子类向父类 向下转型:强制转,有很

黑马程序员--java基础之多态,抽象类,接口,匿名内部类

------Java培训.Android培训.iOS培训..Net培训.期待与您交流! ------- 4.4 子类的实例化过程 子类中所有的构造函数默认都会访问父类中空参数的构造函数. 因为每一个构造函数的第一行都有一条默认的语句super();. 子类会具备父类中的数据,所以要先明确父类是如何对这些数据初始化的. 为什么子类实例化的时候要访问父类中的构造函数呢? 那是因为子类继承了父类,获取到了父类中内容(属性),所以在使用父类内容之前,要先看父类是如何对自己的内容进行初始化的. P.S.