【深入理解多态】从“妈妈我想吃烤山药”讲起

目录

  • 1、从吃烤山药重新认识多态
  • 2、 多态前提条件【重点】
  • 3、 多态的体现
  • 4、向上转型
  • 5、向下转型
  • 6、向上向下转型再次分析【加餐不加价】
  • 7、多态与构造器之间的微妙
  • 8、多态的优点
  • 9、分析开篇的九个问题
  • 10、最后我们一起来正式分析那九个题

@

在我认为多态绝对是面向对象的第三大特性中让很多小白同学以及初学者难以跨越的鸿沟,因为多态有很多细节性的知识,不花点时间,还真不好理解多态。这么说吧,如果你觉得你已经完全理解了多态,你不妨做做下面的程序,如果你能全都答对,那没问题了,多态对你来说真的不是问题!如果在第四个就趴下了,那可以看看这篇文章,或许对你有所帮助,可能会让你重新见识到多态的魅力。

package Polymorphic;
//爷爷类
class Ye {
    public String show(Sun obj) {
        return ("Ye and Sun");
    }

    public String show(Ye obj) {
        return ("Ye and Ye");
    }

}
//爸爸类
class Fu extends Ye {
    public String show(Fu obj) {
        return ("Fu and Fu");
    }

    public String show(Ye obj) {
        return ("Fu and Ye");
    }
}
//儿子类
class Zi extends Fu {

}
//孙子类
class Sun extends Fu {

}

public class PolymorphicTest {
    public static void main(String[] args) {
         Ye y = new Ye();
        Ye y2 = new Fu(); //向上
        Fu f = new Fu();
        Zi z = new Zi();
        Sun s = new Sun();

        System.out.println("第一题 " + y.show(f));
        System.out.println("第二题 " + y.show(z));
        System.out.println("第三题 " + y.show(s));
        System.out.println("第四题 " + y2.show(f));  //到这里挂了???
        System.out.println("第五题 " + y2.show(z));
        System.out.println("第六题 " + y2.show(s));
        System.out.println("第七题 " + f.show(f));
        System.out.println("第八题 " + f.show(z));
        System.out.println("第九题 " + f.show(s));

    }
}

先把答案记在小本本上吧,再对照下面结果看看

第一题 Ye and Ye
第二题 Ye and Ye
第三题 Ye and Sun
第四题 Fu and Ye
第五题 Fu and Ye
第六题 Ye and Sun
第七题 Fu and Fu
第八题 Fu and Fu
第九题 Ye and Sun

如果你对上面的结果很意外,或者不解,那么恭喜你,你又能学到新知识了,成功的向架构师前进了一步!好了,让我们一起重新见识见识多态的魅力吧!

1、从吃烤山药重新认识多态

最近不是正火着吃烤山药么,学习就要走有趣化路线,毕竟兴趣永远最好的老师,咋们放开点,怎么有趣怎么来。

小明妈妈的情绪非常不稳定,心情好的时候巴不得给小明花一个亿,,心情不好的时候巴不得把小明打成麻瓜,可是小明永远不知道妈妈的情绪变化。这不,今天一位老大爷在卖烤山药,边烤还边跳激光雨,嗨得不行,小明特别喜欢激光雨,马上就忍不住了,心里默默想着,刚烤的山药它不香嘛,激光雨烤的山药它不香嘛。于是忍不住对妈妈说:“妈妈,我想吃烤山药”,这个时候,来了,来了,他来了,它真的来了....你激动个锤子啊......是代码来了:

package Polymorphic;

     class  Matcher{
        public void matcherSpeak(){
            System.out.println("想吃烤山药?");
        }
    }

     class HappyMother extends Matcher {
        public void matcherSpeak(){
            System.out.println("开心的妈妈说:吃,吃大块的,一火车够吗");
        }
    }

     class SadMother extends Matcher {
        public void matcherSpeak(){
            System.out.println("不开心的妈妈说:吃你个憨皮,看我回家扎不扎你就完事了");
        }
    }

     class VeryHappyMother extends Matcher {
        public void matcherSpeak(){
            System.out.println("异常开心的妈妈说:买买买,烤山药咱全买了,顺便把大爷也买回家,天天给你表演激光雨(大爷懵逼中)");
        }
    }

    public class UnderstandPolymorphic{
        public static void main(String[] args) {
            Matcher m = new HappyMother();
            m.matcherSpeak();

            m = new SadMother();
            m.matcherSpeak();

            m = new VeryHappyMother();
            m.matcherSpeak();

        }
    }
运行结果:

开心的妈妈说:吃,吃大块的,一火车够吗
不开心的妈妈说:吃你个憨皮,看我回家扎不扎你就完事了
异常开心的妈妈说:买买买,烤山药咱全买了,顺便把大爷也买回家,天天给你表演激光雨(大爷懵逼中)

妈妈听到小明想吃烤山药这同一行为,表现出不同的表现形式,这就是多态。多态专业定义则是:程序中定义的引用变量所指向的具体类型和通过该引用变量发出的方法调用在编程时并不确定,而是在程序运行期间才确定,这种情况叫做多态没错是没错就是脑壳有点大,所以我选择简单点定义多态: 多态指同一行为,具有多个不同表现形式。为何会有如此微妙的变化呢,那我们就必须了解进行多态的前提了。

2、 多态前提条件【重点】

如果多态不能满足以下三个前提条件,那还玩犊子的多态【构不成多态,缺一不可】

  1. 继承或者实现【二选一】
  2. 方法的重写【意义体现:不重写,无意义】
    子类对父类中某些方法进行重新定义,在调用这些方法时就会调用子类的方法。
  3. 父类引用指向子类对象(也可以说向上转型)【体现在格式上】

回过头来看烤山药例子,确实都有继承,同样都重写了motherSpeak()方法,最关键的代码则是

 Matcher m = new HappyMother();

也就是所谓的 父类引用指向子类对象,这其实就是向上转型!对向上转型概念不清晰没事,下面会详细讲解。

3、 多态的体现

多态体现的格式: 父类/父接口类型 变量名 = new 子类对象变量名.方法名();

当使用多态方式调用方法时,首先检查父类中是否有该方法,如果没有,则编译错误 ,如果有,执行的是子类重写后的方法,也就是向上转型时, 子类单独定义的方法丢失问题。编译报错。 代码如下:

package Demo;

class  Matcher{
    public void matcherSpeak(){//=========================父类matcherSpeak()方法
        System.out.println("吃烤山药?");
    }
}

class HappyMother extends Matcher {
    public void matcherSpeak(){//=========================子类matcherSpeak()方法
        System.out.println("开心的妈妈说:吃,吃大块的,一蛇皮袋够吗");
    }

    public void fatherSpeak(){//=========================子类独有的fatherSpeak()方法
        System.out.println("开心的妈妈说:吃,吃大块的,一麻袋够吗");
    }
}
public class Test {
    public static void main(String[] args) {
        Matcher m=new HappyMother();
        m.matcherSpeak();
        m.fatherSpeak();  //编译失败,无法解析fatherSpeak方法
    }
}

分析如下:

当然这个例子只是入门级的,接下来看个有点水平的例子

package Demo;

class  Matcher{
    public void matcherSpeak(){
        System.out.println("想吃烤山药?");
    }

}

class HappyMother extends Matcher {
    public void matcherSpeak(){
        System.out.println("开心的妈妈说:吃,吃大块的,一火车够吗");
    }
}
class SadMother extends HappyMother{
    public void tt(){
        System.out.println("ttttttt");
    }
}
public class Test {
    public static void main(String[] args) {
        Matcher mm=new SadMother();
        mm.matcherSpeak();
    }

运行结果:开心的妈妈说:吃,吃大块的,一火车够吗
}

有了第一个基础这个相信不难理解,接着看

package Demo;

class  Matcher{
    public void matcherSpeak(){
        System.out.println("想吃烤山药?");
    }
}

class HappyMother extends Matcher {

}
class SadMother extends HappyMother{
    public void tt(){
        System.out.println("ttttttt");
    }
}
public class Test {
    public static void main(String[] args) {
        Matcher mm=new SadMother();
        mm.matcherSpeak();
    }

运行结果:想吃烤山药?

}

到这里,再来回味下这句话:

当使用多态方式调用方法时,首先检查父类中是否有该方法,如果没有,则编译错误 ,如果有,执行的是子类重写后的方法

你可能会说子类中都没有这些个方法啊,何来执行子类重写后的方法一说?它好像是去父类中找该方法了。事实上,子类中是有这些方法的,这个方法继承自父类,只不过没有覆盖该方法,所以没有在子类中明确写出来而已,看起来像是调用了父类中的方法,实际上调用的还是子类中的。同学继承方面的知识该补补了,可以参考下面这篇【java基础】java继承从“我爸是李刚”讲起

4、向上转型

向上转型:多态本身是子类类型向父类类型向上转换的过程,其中,这个过程是默认的。你可以把这个过程理解为基本类型的小类型转大类型自动转换,不需要强制转换。 当父类引用指向一个子类对象时,便是向上转型。 向上转型格式:

父类类型 变量名 = new 子类类型(); 如:Father f= new Son();

例子的话,烤山药的例子就是一个典型的向上转型例子

5、向下转型

向下转型:父类类型向子类类型向下转换的过程,这个过程是强制的。同样可以把这个过程理解为基本类型的自动转换,大类型转小类型需要强制转换。一个已经向上转型的子类对象,将父类引用转为子类引用,可以使用强制类型转换的格式,向下转使用格式:

Father father = new Son();
子类类型 变量名 = (子类类型) 父类变量名; 如:Son s =(Son) father;

不知道你们有没有发现,向下转型的前提是父类对象指向的是子类对象(也就是说,在向下转型之前,它得先向上转型),当然,向下转型还是有它的意义所在,下面就讲解向下转型的意义。

到这里,我们讲解一下为什么要向下转型?上面已经讲到过当使用多态方式调用方法时,首先检查父类中是否有该方法,如果没有,则编译错误。也就是说,不能调用子类拥有,而父类没有的方法。编译都错误,更别说运行了。这也是多态给我们带来的一点"小麻烦"。所以,想要调用子类特有的方法,必须做向下转型

package Demo;

class  Matcher{
    public void eat(){
        System.out.println("想吃烤山药?");
    }
}

class XiongHaiZi extends Matcher {
    public void eat(){
        System.out.println("妈妈,我想吃烤山药");
    }

    public void eatSuLi(){//============================子类特有的eatSuLi方法
        System.out.println("麻麻,我想吃酥梨,要吃麻瓜那么大的酥梨");
    }
}

public class Test {
    public static void main(String[] args) {

        Matcher m = new XiongHaiZi();//向上转型

        XiongHaiZi x = (XiongHaiZi)m;//向下转型

        x.eatSuLi();//执行子类特有方法

    }

    运行结果:麻麻,我想吃酥梨,要吃麻瓜那么大的酥梨
}

好了向下转型就讲到这里...等等,你真的以为就讲完了?肯定不行喽,向下转型还有一个要说的知识,讲之前先来看个程序先

package Demo;

class  Matcher{
    public void eat(){
        System.out.println("想吃烤山猪?");
    }

}

class Boy extends Matcher {
    public void eatKaoYang(){
        System.out.println("妈妈,我想吃烤山猪");
    }
}

class Girl extends Matcher {
    public void eatKaoYang(){
        System.out.println("妈妈,我想吃烤山猪2333");
    }
}

public class Test {
    public static void main(String[] args) {

        Matcher g = new Girl();//向上转型编译通过

        Boy x = (Boy)g;//向下转型

        x.eatKaoYang();//编译通过,但运行报ClassCastException

    }

 运行结果:  运行报ClassCastException

}

这段代码可以通过编译,但是运行时,却报出了 ClassCastException ,类型转换异常!这是因为,明明创建了Girl类型对象,运行时,当然不能转换成Boy对象的。这两个类型并没有任何继承关系,不符合类型转换的定义。 为了避免ClassCastException的发生,Java提供了 instanceof 关键字,给引用变量做类型的校验。

instanceof的使用

instanceof 的格式:
变量名 instanceof 数据类型

instanceof 的使用
如果变量属于该数据类型,返回true。
如果变量不属于该数据类型,返回false。

所以,转换前,我们最好使用instanceof 先做一个判断,代码如下:

package Demo;

class  Matcher{
    public void eat(){
        System.out.println("想吃烤山药?");
    }

}

class Boy extends Matcher {
    public void eatKaoYang(){
        System.out.println("Boy:妈妈,我想吃烤羊");
    }
}

class Girl extends Matcher {
    public void eatKaoYang(){
        System.out.println("Girl:妈妈,我想吃烤全羊2333");
    }
}

public class Test {
    public static void main(String[] args) {

        Matcher g = new Girl();//向上转型

        if(g instanceof Girl){
            Girl x = (Girl)g;//向下转型
            x.eatKaoYang();  //=====================调用Girl的eatKaoYang()方法
        }else if(g instanceof Boy){ //不执行
            Boy x = (Boy)g;//向下转型
            x.eatKaoYang();  //=====================调用Boy的eatKaoYang()方法
        }
    }
}

运行结果: Girl:妈妈,我想吃烤全羊2333

好了到这里,你get到了咩?

6、向上向下转型再次分析【加餐不加价】

看完之后是不是还是不够清晰向上向下转型?多态转型问题其实并不复杂,只要记住一句话:父类引用指向子类对象。那什么叫父类引用指向子类对象?看下面例子吧

有两个类,Father 是父类,Son 类继承自 Father

第 1 个例子:

//  f1 引用指向一个Son对象
Father f1 = new Son();   // 这就叫 upcasting (向上转型)
// f1 还是指向 Son对象
Son s1 = (Son)f1;   // 这就叫 downcasting (向下转型)

第 2 个例子:

// f1现在指向father对象
Father f2 = new Father();
Son s2 = (Son)f2;       // 出错,子类引用不能指向父类对象

你或许会问,第1个例子中:Son s1 = (Son)f1; 为什么是正确的呢。很简单因为 f1 指向一个子类对象,Father f1 = new Son(); 子类 s1 引用当然可以指向子类对象了。

f2 被传给了一个 Father 对象,Father f2 = new Father(); 子类 s2 引用不能指向父类对象。

7、多态与构造器之间的微妙

直接上代码:

package Polymorphic;

class EatKaoShanYao {
    EatKaoShanYao () {
        System.out.println("吃烤山药之前...");
        eat();
        System.out.println("吃烤山药之后(熊孩子懵逼中)....");
    }
    public void eat() {
        System.out.println("7岁半就喜欢吃烤山药");
    }
}
public class KaoShanYao extends EatKaoShanYao {
    private String Weight = "110斤";
    public KaoShanYao(String Weight) {
        this.Weight = Weight;
        System.out.println("熊孩子的体重:" + this.Weight);
    }

    public void eat() { // 子类覆盖父类方法
        System.out.println("熊孩子吃烤山药之前的体重是:" + this.Weight);
    }

    //Main方法
    public static void main(String[] args) {
           EatKaoShanYaok = new KaoShanYao("250斤");

    }
}

童鞋们可以试想一下运行结果,再看下面的输出结果

 运行结果:
                吃烤山药之前...
                熊孩子吃烤山药之前的体重是:null
                吃烤山药之后(熊孩子懵逼中)....
                熊孩子的体重:250斤

是不是很疑惑?结果为啥是这样?你看,熊孩子又懵逼了,Why?

原因其实很简单,因为在创建子类对象时,会先去调用父类的构造器,而父类构造器中又调用了被子类覆盖的多态方法,由于父类并不清楚子类对象中的属性值是什么(先初始化父类的时候还没开始初始化子类),于是把String类型的属性暂时初始化为默认值null,然后再调用子类的构造器(这个时子类构造器已经初始Weight属性,所以子类构造器知道熊孩子的体重Weight是250)。

如果有什么不理解的可以及时告诉我,楼主一直都在,还有如果楼主哪里写错了或者理解错了,请及时告诉我,一定要告诉我!!!

8、多态的优点

讲了这么久的多态,我觉得其优点已经不明觉厉了。但是还是来聊聊多态在实际开发的过程中的优点。在实际开发中父类类型作为方法形式参数,传递子类对象给方法,进行方法的调用,更能体现出多态的扩展 性与便利。
为了更好的对比出多态的优点,下面程序不使用多态,代码如下:

package Demo;
//父类:动物类
class Animal{
    public void eat(){
        System.out.println("eat");
    }
}
//猫类
class Cat {
    //方法重写
    public void eat(){
        System.out.println("猫吃猫骨头");
    }
    public void call(){
        System.out.println("猫叫");
    }
}
//狗类
class Dog {
    public void eat(){
        System.out.println("狗吃狗骨头");
    }
    public void call(){
        System.out.println("狗叫");
    }
}

//针对动物操作的工具类
class AnimalTool{

    private AnimalTool(){}//把工具类的构造方法私有,防止别人创建该类的对象。

    //调用猫的功能
    public static void catLife(Cat c){  //工具类,方法就写成static的,然后直接在测试类:工具类名.方法 使用。
        c.eat();
        c.call();
    }
    //调用狗的功能
    public static void dogLife(Dog d){
        d.eat();
        d.call();
    }
}

public class Test{
    public static void main(String[] args){

        Cat c= new Cat();
        AnimalTool.catLife(c);

        Dog d= new Dog();
        AnimalTool.dogLife(d);

    }
}
运行结果:
        猫吃猫骨头
        猫叫
        狗吃狗骨头
        狗叫

这里只写了两只动物,如果再来一种动物猪,则需要定义个猪类,提供猪的两个方法,再到工具类中添加对应的XXLife方法,这三步都是必须要做的,而且每多一种动物就需要在工具类中添加一种一个对应的XXLife方法,这样维护起来就很麻烦了,毕竟动物种类成千上万!崩溃吧,没事多态来拯救你,如下使用多态代码:

package Demo;
//父类:动物类
class Animal{
    public void eat(){
        System.out.println("eat");
    }
    public void call(){
        System.out.println("call");
    }
}
//猫类
class Cat extends Animal {
    //方法重写
    public void eat(){
        System.out.println("猫吃猫骨头");
    }
    public void call(){
        System.out.println("猫叫");
    }
}
//狗类
class Dog extends Animal {
    public void eat(){
        System.out.println("狗吃狗骨头");
    }
    public void call(){
        System.out.println("狗叫");
    }
}

//针对动物操作的工具类
class AnimalTool{

    private AnimalTool(){}//最好把工具类的构造方法私有,防止别人创建该类的对象。该类是工具类。

    //调用所以动物的功能
    public static void animalLife(Animal a){  //工具类,方法就写成static的,然后直接在测试类:工具类名.方法 使用。
        a.eat();
        a.call();
    }

}

public class Test{
    public static void main(String[] args){

        Cat c= new Cat();
        AnimalTool.animalLife(c);

        Dog d= new Dog();
        AnimalTool.animalLife(d);
运行结果:
        猫吃猫骨头
        猫叫
        狗吃狗骨头
        狗叫
    }
}

注意: 上面动物类都继承了animal父类

这个时候再分析,如果再来一种动物猪,则需要定义个猪类,提供猪的两个方法,再继承Animal父类,这个时候就不需要在工具类中添加对应的XxLife方法,只写一个animalLife方法即可,而且每多一种动物都不需要在工具类中添加对应的XxLife方法,这样维护起来就很乐观了。

由于多态特性的支持,animalLife方法的Animal类型,是CatDog的父类类型,父类类型接收子类对象,当 然可以把Cat对象和Dog对象传递给方法。 当eatcall方法执行时,多态规定,执行的是子类重写的方法,那么效果自然与Animal的子类中的eatcall方法一致, 所以animalLife完全可以替代以上两方法。 不仅仅是替代,在扩展性方面,无论之后再多的子类出现,我们都不需要编写XxLife方法了,直接使用 animalLife就可以完成。 所以,多态的好处,体现在可以使程序编写的更简单,并有良好的扩展。

9、分析开篇的九个问题

看到这里,相信童鞋们多多少少都应该对多态都一定的了解了,都应该很有信心解决开篇的难题了吧,我可以很负责的告诉你,文章看到这里,你依旧解决不了这几个问题,不要问我为啥知道,你可以试着再做一遍,代码贴在下面:

package Polymorphic;
//爷爷类
class Ye {
    public String show(Sun obj) {
        return ("Ye and Sun");
    }

    public String show(Ye obj) {
        return ("Ye and Ye");
    }

}
//爸爸类
class Fu extends Ye {
    public String show(Fu obj) {
        return ("Fu and Fu");
    }

    public String show(Ye obj) {
        return ("Fu and Ye");
    }
}
//儿子类
class Zi extends Fu {

}
//孙子类
class Sun extends Fu {

}

public class PolymorphicTest {
    public static void main(String[] args) {
         Ye y = new Ye();
        Ye y2 = new Fu(); //向上
        Fu f = new Fu();
        Zi z = new Zi();
        Sun s = new Sun();

        System.out.println("第一题 " + y.show(f));
        System.out.println("第二题 " + y.show(z));
        System.out.println("第三题 " + y.show(s));
        System.out.println("第四题 " + y2.show(f));  //到这里挂了???
        System.out.println("第五题 " + y2.show(z));
        System.out.println("第六题 " + y2.show(s));
        System.out.println("第七题 " + f.show(f));
        System.out.println("第八题 " + f.show(z));
        System.out.println("第九题 " + f.show(s));

    }

打印结果:
    第一题 Ye and Ye
    第二题 Ye and Ye
    第三题 Ye and Sun
    第四题 Fu and Ye
    第五题 Fu and Ye
    第六题 Ye and Sun
    第七题 Fu and Fu
    第八题 Fu and Fu
    第九题 Ye and Sun
}

要想理解上面这个例子,童鞋们必须读懂这句话:当父类对象引用变量引用子类对象时,被引用对象的类型决定了调用谁的成员方法,引用变量类型决定可调用的方法。首先会先去可调用的方法的父类中寻找,找到了就执行子类中覆盖的该方法,就算子类中有现成的该方法,它同样也会去父类中寻找,早到后未必执行子类中有现成的方法,而是执行重写在父类中找到的方法的子类方法(这里的子类也就是最后决定调用的类方法)。你是不是晕了?读着都觉得拗口,要理解可就拗的不是口了而是拗头 ~啥玩意没听过这个词~ 咳咳,问题不大,楼主来通俗的给大家讲解,让大家理解。

还记不记得楼主之前定义向上转型是怎么定义的?

【v8提示】 向上转型:多态本身是子类类型向父类类型向上转换的过程,其中,这个过程是默认的。你可以把这个过程理解为基本类型的小类型转大类型自动转换,不需要强制转换。 当父类引用指向一个子类对象时,便是向上转型

可是,你真的理解了咩?什么叫做父类对象引用变量引用子类对象?其实还得从下面这句话找头绪

向上转型定义:多态本身是子类类型向父类类型向上转换的过程,其中,这个过程是默认的

就好比Father f = new Son();有的童鞋就会说这个f也算是父类的对象引用?如果按字面理解是子类的引用只不过该引用的类型为Father类型?这时你就大错特错了。

我们把向上转型定义简化一下理解一下,简化如下:

子类类型默认向父类类型向上转换的过程

现在明白了咩?这句话可以理解为Father f = new Son()这句代码原本是Father f = (Father )new Son()这样子的只是这个转换过程是默认自动转的,总的来说也就是 new Son()其实本质就是new Father,所以f其实就是父类对象引用!这个时候再来拆开理解下面这段话

当父类对象引用变量引用子类对象时

其中父类对象引用变量指的就是f子类对象指的就是new Son(),所以加起来就是当f引用new Son()

被引用对象的类型决定了调用谁的成员方法,引用变量类型决定可调用的方法。

这里的 被引用对象的类型则是指new Son()对象中的Son类型, 引用变量类型则是指f的类型Father类型

好了总结关联起来就是当:f引用new Son()时,Son决定了调用它的成员方法,Father决定可调用Father中的方法。所以以Father f = new Son()举例,简单来说就是

10、最后我们一起来正式分析那九个题

前三个并没有涉及到多态(向上转型),所以只会调用yeye本类的方法,这里只要掌握继承的知识就OK了。

讲解第四题之前,你的答案是不是"Fu and Fu"?来了喔,马上让你巅覆对多态的人生观!

分析第四题,首先Ye y2 = new Fu(); 向上转型了,所以首先会去Fu类的父类Ye类中找show(f)方法,找到了show(Ye obj)方法,之后回到Fu类中看是否有show(Ye obj)重写方法,发现Fu类有show(Ye obj)方法(重写),所以最后执行了"Fu and Ye",你get了咩?

分析第五题,其实第五题和第四题基本差不多,第四题是y2.show(f);第五题是y2.show(z);只是show的方法参数不同,相同的是fzYe类中找的都是show(Ye obj)方法,所以,最终第四题和第五题结果一致!

分析第六题,第六题其实挺有趣,首先y2.show(s),到Ye类中找到show(Sun obj),之后在子类中看有没有重写,发现并没有show(Sun obj)重写方法,确定没有咩?别忘了这是继承,子类Fu中默认有着父类Ye的方法,只是没有表面表示出来,从另一角度出发,Fu类中默认重写了一个show(Sun obj)方法,就算不写也是存在的,所以运行结果为"Ye and Sun"

第七、八题就不分析了,毕竟也没有涉及到向上转型(多态)。

最后分析一下第九题,有的童鞋就要打楼主了,第九题不也是没有涉及到向上转型(多态)吗,楼主你个星星(**),当然,楼主就算背着黑锅也要分析第九题~就是这么傲娇~,确实没有涉及到向上转型(多态),我要讲的原因很简单,因为我觉得还是很有必要!首先f.show(s)不涉及多态,它只会调用自己类(Fu)的方法,但是你会发现Fu中并没有show(s),唉唉唉,我运行你重新组织下语言,又忘了?这是继承啊,它有默认父类Ye中的show(Sun obj)方法鸭!好了到这里,我总结出一点,你多态以及没得问题了,不过你继承方面知识薄弱啊,不行不行楼主得给你补补,还在犹豫什么鸭,快来补补继承知识!!!【java基础】java继承从“我爸是李刚”讲起

本文的最后,我只是个人对多态的理解,楼主只是个java小白,叫我老白也行,不一定全部正确,如果有什么错误请一定要告诉我,感激不尽感激不尽感激不尽!!!欢迎指正~

最后的最后,如果本文对你有所帮助就点个爱心支持一下吧 ~佛系报大腿~

欢迎各位关注我的公众号,一起探讨技术,向往技术,追求技术...

原文地址:https://www.cnblogs.com/yichunguo/p/11790201.html

时间: 2024-10-28 23:17:17

【深入理解多态】从“妈妈我想吃烤山药”讲起的相关文章

天哪,我只是想吃顿红烧肉啊

前言 今天突然想吃红烧肉了,然后问大厨朋友怎么做,大厨哥们见你学艺诚意蛮高,于是扔过来两本书:   还能好好做朋友吗?你心里万马奔腾,但还没等吐槽出来,大厨哥们就很专业的分析:肉质是口感的关键,酱油是色味的保证,你基础不牢怎么能做出好的红烧肉呢? 站在前人的肩膀上 HTML.CSS.JavaScript是前端的根基,这是无可否认的事实.正如一辆车当然都是由一堆钢板和螺钉组成的,但是现在还有人拎着个锤子敲敲打打的造车吗?李书福说过,"汽车不过是四个轮子加两个沙发",去一趟家具城和轮胎店,

OSChina 周一乱弹 —— 自古逢秋悲寂寥,突然我想吃辣条

啦啦啦,周一又来啦,大家伙赶紧努力 干 活 啦! 昨天普及了一下[辣条]的相关知识,辣条不仅仅得到了歪果仁的认可,还得到了俺们美丽的大菲哥的认可: @大菲 : 自古逢秋悲寂寥,突然我想吃辣条. 无论开森还是快乐,俺们大菲哥始终走在潮流的最前线- 啥???北京的淫民不开森? 北京又沦陷了.一记者街头采访:"大娘,你觉得雾霾给你的生活带来什么影响?" 大娘:"你先看清楚,我是你大爷!" 大爷,你还好吗? 很多在北京工作的 OSCer 就想着,要不离开这个雾都?都来奔向

如何理解云计算?很简单,就像吃货想吃披萨了

你一定听说过云计算中的三个"高大上"的你一定听说过云计算中的三个"高大上"的概念:IaaS.PaaS和SaaS,这几个术语并不好理解.不过,如果你是个吃货,还喜欢披萨,这个问题就好解决了! 一个"吃货"是怎样吃到披萨的呢? 1.在家自己做 这真是个麻烦事,你的准备很多东西,发面.做面团.进烤箱.....简单列一下,需要下图所示的一切: 2.买好速食披萨回家自己做着吃 你只需要从披萨店里买回成品,回家烘焙就好了,在自己的餐桌上吃.和自己在家做不同,

【转】如何理解云计算?很简单,就像吃货想吃披萨了

你一定听说过云计算中的三个"高大上"的概念:IaaS.PaaS和SaaS.这几个术语并不好理解.不过,如果你是个吃货,还喜欢披萨,这个问题就好解决了!好吧,其实你根本不是一个吃货,之所以自我标榜为吃货,其实是为了收获赞叹式的夸奖,"吃货还这么瘦,好羡慕啊!"或者,总得给伦家的微丰找个像样的理由. 一个"吃货"是怎样吃到披萨的呢? 1. 在家自己做 这真是个麻烦事,你的准备很多东西,发面.做面团.进烤箱.....简单列一下,需要下图所示的一切: 2

再看“陈伟视频”理解多态机制 ——你还记得“橘子,苹果的例子么”

"接口你觉得是什么?"在我没有回复这集视频之前我会直接说解耦.为什么是解耦,可能我当时觉得只要在B层和D层中穿插一层,那么B层就不会直接调用D层的类的方法,这样子就达到了解耦的效果.后来我才发现了我敲代码的一个误区,那就是我在敲机房收费系统的时候仅仅按照七层的样式,每定义一个接口的时候就实现一个类,导致我的理解就是一个接口仅仅对应一个类.但是接口仅仅只是这样子的么? 我想陈伟视频49集给了我一个很好的启发--多态. [回顾] 有个只进不出的盒子,水果有两种苹果和香蕉,原始重量分别是50

理解多态AND理解父类引用指向子类对象

假设现在有一个父类Father,它里面的变量需要占用1M内存.有一个它的子类Son,它里面的变量需要占用0.5M内存. 现在通过代码来看看内存的分配情况: Father f = new Father();//系统将分配1M内存. Son s = new Son();//系统将分配1.5M内存. 因为子类中有一个隐藏的引用super会指向父类实例,所以在实例化子类之前会先实例化一个父类,也就是说会先执行父类的构造函数. 由于s中包含了父类的实例,所以s可以调用父类的方法. Son s1 = s;/

Java--再次理解多态

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

深入理解多态与方法的绑定

话不多说,先上源码 public class olymorphism { public static void main(String[] args) { Shap circle = new Circle(); Painter painter = new Painter(); painter.paint(circle); } } class Shap{ void draw(){ System.out.println("shap.draw()"); } } class Circle ex

深入理解多态

1.实现多态的三种方式   1.1 虚方法   1.2 抽象类   1.3 接口2.里氏替换   2.1 里氏替换的概念:在一个软件系统中,如果子类替换父类出现的位置,而对整个软件系统功能没有影响.3.抽象类和抽象方法   虚方法可以选择性的被子类重写,抽象方法强制子类必须重写   3.1 一个类中abstract修饰,就是抽象类   3.2 抽象类不能实例化(换个说法就是 不能new)   3.3 抽象方法不能有方法体,甚至连{}都不能有   3.4 抽象方法只能存在于抽象类中   3.5 抽