20.面向对象进阶:多态,强制类型转换,instanceof关键字,接口实现类多态综合案例

面向对象三大基本特征之三:多态

多态的概述,多态的形式

  • 多态的定义:

    • 同类型的对象,执行同一个行为,会表现出不同的行为特征
  • 多态的常见形式:

    • 父类类型 对象名称 = new 子类构造器;
      接口 对象名称 = new 实现类构造器;
      
    • 测试案例:

      package com.java.PolymorphicTest;
      
      public abstract class Animal {
          // 为方便测试统一采用public
          public String name = "父类动物";
      
          public abstract void run();
      }
      
      package com.java.PolymorphicTest;
      
      public class Dog extends Animal {
      
          public String name = "子类狗";
      
          @Override
          public void run() {
              System.out.println("小狗跑的很快!");
          }
      }
      
      package com.java.PolymorphicTest;
      
      public class Tortoise extends Animal {
          public String name = "子类乌龟";
      
          @Override
          public void run() {
              System.out.println("乌龟跑的很慢!");
          }
      }
      
      package com.java.PolymorphicTest;
      
      public class Test {
          public static void main(String[] args) {
              Animal d = new Dog();
              d.run();
              System.out.println(d.name);
              System.out.println("========================");
              Animal t = new Tortoise();
              t.run();
              System.out.println(d.name);
      
              System.out.println("============多态第二个优势,父类作为方法形参,可以将所有子类对象传进来============");
              go(d);
              go(t);
          }
      
          /**
           * 多态第二个优势
           */
          public static void go(Animal a) {
              a.run();
          }
      }
      /*
      小狗跑的很快!
      父类动物
      ========================
      乌龟跑的很慢!
      父类动物
      ============多态第二个优势,父类作为方法形参,可以将所有子类对象传进来============
      小狗跑的很快!
      乌龟跑的很慢!
      
       */
      
  • 多态中成员访问特点:

    • 成员方法调用:编译看左边,运行看右边

      编译的时候会看向左边父类的成员方法,具体运行时会运行右边子类对象的方法

    • 成员变量调用:编译看左边,运行也看左边(多态是侧重行为的多态)

      成员变量的调用,不管编译还是运行,都看向父类的一方(如上述测试代码name的值均为父类动物)

  • 多态的前提:

    • 有继承/实现关系
    • 有父类引用指向子类对象
    • 有方法重写

多态的好处

  • 在多态形势下,右边对象可以实现解耦合,便于扩展和维护

    组件化的解耦合即组件之间的依赖没有那么强

    Animal a = new Dog();
    a.run();
    
    //当Dog类换成其他类,即对象发生了变化,后续业务行为随对象而变,后续的代码逻辑无需修改
    
    Animal a = new Cat();
    a.run();
    
  • 定义方法的时候,使用父类型作为参数,该方法就可以接收这父类的一切子类对象,体现出多态的扩展性与便利

    参照上方测试案例代码


  • 多态下会产生的一个问题:
    • 多态下不能使用 子类的独有功能

多态下引用数据类型的类型转换

为了解决多态下不能调用子类独有功能的弊端提出引用数据类型的类型转换

  • 自动类型转换(从子到父):子类对象可以直接赋值给父类类型的变量指向

  • 强制类型转换(从父到子):

    • 此时必须进行强制类型转换:子类 对象变量 = (子类)父类类型的变量

    • 作用:可以解决多态下的劣势,可以实现调用子类独有的功能

    • 注意:如果转型后的类型和对象真实类型不是同一种类型,那么在转换的时候就会出现ClassCastException

      强制类型转换,编译阶段不报错的(注意:有继承或者实现关系编译阶段可以强制转换,但是运行时可能出错)

      例如,Dog d = (Dog) a2;编译器认为程序员希望将Animal类的对象a2转换成Dog类型并指向Dog类型的d,因此编译阶段放行,但是在运行时发现,d不是Dog的对象,而是乌龟的对象,指向有问题,张冠李戴,于是报错

    • Java建议强转前使用instanceof关键字判断当前对象的真实类型,然后再强制转换

      变量名 instanceof 真实类型
      // 判断关键字左边的变量指向的对象的真实类型,是否是右边的类型或者是其子类型,是则返回true,反之返回false
      
  • 测试代码:

    package com.java.PolymorphicDisadvantages;
    
    public class Animal {
        public void run(){
            System.out.println("动物可以跑!");
        }
    }
    
    package com.java.PolymorphicDisadvantages;
    
    public class Dog extends Animal {
        @Override
        public void run() {
            System.out.println("🐕跑的快");
        }
    
        /**
         * 独有功能
         */
        public void lookDoor() {
            System.out.println("🐕看门");
        }
    
    }
    
    package com.java.PolymorphicDisadvantages;
    
    
    public class Tortoise extends Animal {
        @Override
        public void run() {
            System.out.println("🐢跑的很慢!");
        }
    
        /**
         * 独有功能
         */
        public void layEggs() {
            System.out.println("🐢可以下蛋");
        }
    }
    
    
    package com.java.PolymorphicDisadvantages;
    
    public class Test {
        public static void main(String[] args) {
            // 自动类型转换
            Animal a = new Dog();
            a.run();
    
            // 强制类型转换
            Animal a2 = new Tortoise();
            Tortoise t = (Tortoise) a2;
            Dog d = (Dog) a;
            d.lookDoor();
            t.layEggs();
    
            System.out.println(d instanceof Dog);   // true
            System.out.println(t instanceof Tortoise);  // true
            System.out.println(d instanceof Tortoise);   // 在编译阶段直接报红,由此可知类型不匹配
    
        }
    }
    

多态的综合案例

  • 需求:

    • 使用面向对象编程模拟:设计一个电脑对象,可以安装2个USB设备
    • 鼠标:被安装时可以完成接入,调用点击功能,拔出功能
    • 键盘:被安装时可以完成接入,调用打字功能,拔出功能
  • 分析:

    1. 定义一个USB接口(申明USB设备的规范是:可以接入和拔出)
    2. 提供两个USB实现类代表鼠标和键盘,让其实现USB接口,并分别定义独有功能
    3. 创建电脑对象,创建2个USB实现类对象,分别安装到电脑中并触发功能的执行
  • 案例代码:

    package com.java.Polymorphic_Case;
    
    /**
     * usb接口规范
     */
    public interface USB {
        void connect();
    
        void unconnect();
    }
    
    
    package com.java.Polymorphic_Case;
    
    /**
     * 鼠标实现类
     */
    public class Mouse implements USB {
        private String goodsName;
    
        public Mouse() {
        }
    
        public Mouse(String goodsName) {
            this.goodsName = goodsName;
        }
    
        public String getGoodsName() {
            return goodsName;
        }
    
        public void setGoodsName(String name) {
            this.goodsName = name;
        }
    
        @Override
        public void connect() {
            System.out.println(getGoodsName() + "鼠标成功接入");
        }
    
        @Override
        public void unconnect() {
            System.out.println(getGoodsName() + "鼠标成功断开");
        }
    
        /**
         * 鼠标独有功能:点击
         */
        public void onClick() {
            System.out.println("使用" + getGoodsName() + "鼠标点击了IDEA中的运行按钮");
        }
    }
    
    
    package com.java.Polymorphic_Case;
    
    import java.util.Scanner;
    
    /**
     * 键盘实现类
     */
    public class KeyBoard implements USB {
        private String goodsName;
    
        public KeyBoard() {
        }
    
        public KeyBoard(String goodsName) {
            this.goodsName = goodsName;
        }
    
        public String getGoodsName() {
            return goodsName;
        }
    
        public void setGoodsName(String goodsName) {
            this.goodsName = goodsName;
        }
    
        @Override
        public void connect() {
            System.out.println(getGoodsName() + "键盘成功接入");
        }
    
        @Override
        public void unconnect() {
            System.out.println(getGoodsName() + "键盘成功断开");
        }
    
        /**
         * 键盘独有功能:打字
         */
        public void keyDone() {
            Scanner scanner = new Scanner(System.in);
            System.out.println("请敲击键盘");
            String str = scanner.next();
            System.out.println("使用" + getGoodsName() + "键盘敲击出一串内容:" + str);
        }
    }
    
    
    package com.java.Polymorphic_Case;
    
    public class Computer {
        private String goodsName;
    
    
        public Computer() {
        }
    
        public Computer(String goodsName) {
            this.goodsName = goodsName;
        }
    
        public String getGoodsName() {
            return goodsName;
        }
    
        public void setGoodsName(String goodsName) {
            this.goodsName = goodsName;
        }
    
        /**
         * 提供安装USB设备的入口
         */
        public void installUSB(USB usb) { //多态用法
            usb.connect();
    
            // 独有功能调用
            if (usb instanceof KeyBoard) {
                KeyBoard k = (KeyBoard) usb;
                k.keyDone();
            } else if (usb instanceof Mouse) {
                Mouse m = (Mouse) usb;
                m.onClick();
            }
    
            usb.unconnect();
        }
    
        public void start() {
            System.out.println(getGoodsName() + "电脑开机了");
        }
    }
    
    
    package com.java.Polymorphic_Case;
    
    public class Test {
        public static void main(String[] args) {
            Computer c = new Computer();
            c.setGoodsName("Thinkpad");
            c.start();
    
            USB u = new KeyBoard("HHKB");
            c.installUSB(u);
    
            USB v = new Mouse("Razer");
            c.installUSB(v);
        }
    }
    
    
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 217,185评论 6 503
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,652评论 3 393
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 163,524评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,339评论 1 293
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,387评论 6 391
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,287评论 1 301
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,130评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,985评论 0 275
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,420评论 1 313
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,617评论 3 334
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,779评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,477评论 5 345
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,088评论 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,716评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,857评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,876评论 2 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,700评论 2 354

推荐阅读更多精彩内容