• 面向对象之多态


    前言

    二胖学习完多态之后,他发现可以将之前学的封装、继承和接口的知识点也融合进来。二胖按照惯例也将这些知识点总结下来方便日后复习补充。

    正文

    为了更好地学习多态,我们需要先了解继承和接口。关于继承和接口,我都单独写过文章:面向对象之继承面向对象之抽象类和接口,欢迎大家阅读与交流。有了这些知识点的铺垫,我们就可以很好地理解多态了。

    什么是多态?我们为什么要使用它?

    "多态",顾名思义就是"多种状态",对多态的定义不同文档的描述都不尽相同。简单来说:就是一个对象对应着不同类型。具有这种性质的例子在生活中很常见:

    
    class Animal
    {}
    
    class Cat extends Animal
    {}
    
    class Dog extends Animal
    {}
    
    Cat x = new Cat();
    
    Animal x = new Cat();
    
    

    上面代码中的x对象就既拥有猫的特性又拥有动物的特性,这就是多态。我们接下来就可以思考为什么会有这种性质的出现呢?其实我们可以逆向思考:如果没有这种性质会出现什么情况,比如考虑下面的例子:

    
    class Mouse   // 鼠标类
    {
    }
    
    class BookPC   // 笔记本类
    {
    	public static void main(String[] args)
    	{
    		useMouse(new Mouse());
    	}
    
    	public static void useMOuse(Mouse m)
    	{
    		System.out.println("鼠标启动了");
    	}
    }
    
    

    我们知道现在的笔记本电脑远不止可以使用鼠标这一个功能,如果后续我们需要使用硬盘,上面的代码就必须修改为:

    
    class Mouse
    {
    }
    
    class Upan  // U盘类
    {
    }
    
    class BookPC
    {
    	public static void main(String[] args)
    	{
    		useMouse(new Mouse());
    		useUpan(new Upan());
    	}
    	public static void useUpan(Upan u)
    	{
    		System.out.println("U盘启动了");
    	}
    
    
    	public static void useMOuse(Mouse m)
    	{
    		System.out.println("鼠标启动了");
    	}
    }
    
    

    如果后续我们还要使用光盘... ...还得像上面那样修改代码,很明显,这样的代码扩展性很差。那么怎样解决呢?其实这就是第一段代码为什么要给Cat赋予Animal特性的原因。

    总结起来,多态的使用提高了代码的扩展性,但同时我们也需要注意,Animal类是不能调用Cat类的方法的(这在后面说到instanceof时还会提到)。

    多态在Java中的体现?

    通过上面的几段代码我们其实就可以总结出多态在java中的几种实现方式:

    • 通过继承类:具体是指父类的引用变量可以指向本类的实例对象也可以指向其子类的实例对象。这种实现方式其实是通过方法覆盖完成的。

    • 通过实现接口:具体是指接口的引用变量可以指向其不同实现类的实例对象。这种实现方式也是通过方法覆盖完成的。

    • 通过重载:具体是指同一类中有多个同名方法,但是它们的参数都不同。

    其实前两种方式属于运行时多态,而最后一种属于编译时多态(因为它在编译阶段就能确定调用的是哪个方法)。而运行时多态最终都是让类与类之间产生关系从而为多态创造前提条件。

    多态需要满足的前提条件?

    其实通过上面的分析与陈述,我们其实可以看出实现多态是需要一些条件的。

    • 首先为了让类与类之间产生关系,需要继承或者实现接口

    • 需要有覆盖的操作

    instanceof关键字?

    在解释instanceof关键字之前,我们需要先知道"自动类型提升""向下转型"这两个概念:

    
    abstract class Animal
    {
        abstract void eat();
    }
    
    class Cat extends Animal
    {
        void eat()
        {
            System.out.println("吃鱼");
        }
        void catchMouse()
        {
            System.out.println("抓老鼠");
        }
    }
    
    class Demo {
        public static void main(String[] args) {
            Animal a = new Cat();   // 自动类型提升:猫对象提升为了动物类型。但是猫对象中的特有功能就无法访问了。
    
            Cat c = (Cat)a;  // 如果还想用动物猫的特有功能,可以将该对象进行向下转型。
        }
    }
    
    

    我们需要注意的是上面代码中的向下转型,因为如果转换不当就可能抛出ClassCastException。(这其实很好理解:Cat类的a对象是不可能转换成Pig类的对象的)所以为了加强代码的健壮性,我们在进行向下转型之前可以先使用instanceof进行判断。就像下面这段代码一样(关于抽象类的介绍,可以查看我的另一篇博客面向对象之抽象类和接口):

    
    abstract class Animal
    {
        abstract void eat();
    }
    
    class Cat extends Animal
    {
        void eat()
        {
            System.out.println("吃鱼");
        }
        void catchMouse()
        {
            System.out.println("抓老鼠");
        }
    }
    
    class Pig extends Animal
    {
        void eat()
        {
            System.out.println("饲料");
        }
        void gongDi()
        {
            System.out.println("拱地");
        }
    }
    
    class Demo {
        public static void main(String[] args) {
            method(new Cat());
    
            method(new Pig());
        }
    
        public static void method(Animal a)
        {
            if (a instanceof Cat)
            {
                Cat c = (Cat) a;
                c.catchMouse();
            }
    
            if(a instanceof Pig) {
                Pig p = (Pig) a;
                p.gongDi();
            }
        }
    }
    
    

    多态时各成员的特点?

    成员变量

    
    class Fu
    {
        int num = 3;
    }
    
    class Zi extends Fu
    {
        int num = 4;
    }
    
    class  Demo3
    {
        public static void main(String[] args)
        {
            Fu f = new Zi();
            System.out.println(f.num);
        }
    }
    
    

    由上面代码的运行结果我们可以看出:对于成员变量,编译和运行时参考的都是引用变量所属的类

    成员函数

    
    class Fu
    {
        void show()
        {
            System.out.println("fu show");
        }
    }
    
    class Zi extends Fu
    {
        void show()
        {
            System.out.println("zi show");
        }
    }
    
    class  Demo3
    {
        public static void main(String[] args)
        {
            Fu f = new Zi();
            f.show();
        }
    }
    
    

    由上面代码的运行结果我们可以看出:对于成员函数,编译时参考的是引用变量所属的类,而运行时参考的是对象所属的类

    静态函数

    
    class Fu
    {
        static void method()
        {
            System.out.println("fu static method");
        }
    }
    
    class Zi extends Fu
    {
        static void method()
        {
            System.out.println("zi static method");
        }
    }
    
    class  Demo3
    {
        public static void main(String[] args)
        {
            Fu f = new Zi();
            f.method();
        }
    }
    
    

    由上面代码的运行结果我们可以看出:对于静态函数,编译和运行时参考的都是引用变量所属的类。而且我们还能从中看出:静态函数是不能被覆盖的

    结束语

    在正文的结束,我想向大家推荐一个介绍多态非常形象的例子:动物王国的面向对象。同时也向大家推荐这个作者的公众号,他以一些有趣的例子和语言来讲解技术,非常生动形象。

  • 相关阅读:
    WebSocket 实战--转
    ELK原理与介绍
    linux命令:使用less从后向前查看日志信息
    Unity创作赛车游戏的四款插件
    Dolly
    shot
    Unity3D获取Android平台的电量
    Unity制作王者荣耀商业级手游
    unity热更新AssetBundle框架设计_框架篇
    喷气传动
  • 原文地址:https://www.cnblogs.com/syhyfh/p/12468088.html
Copyright © 2020-2023  润新知