重写 多态 向上转型和向下转型 究极易理解篇

前言:在看完狂神说Java的关于重写 多态 向上转型和向下转型的视频之后,我感到了极大的困惑和难以理解,于是我去看了很多不同的视频,最终总结出一套究极容易理解和运用的模式
用心看完,你也应该会有很大的收获
PS:有些代码我使用图片的形式上传,为的是是结果更加清晰易懂,可以配合Ctrl+滚轮食用


方法的重写(override)

重写的条件

  • 当父类方法无法满足子类的业务需求
  • 重写需要有继承关系,子类重写父类的方法
  • 方法名必须相同,参数列表相同(否则变成重载了),方法体不同,返回值类型相同
  • 抛出的异常不能更多,可以更少
  • 访问权限不能更低,可以相同或更高 public最高…
  • Alt+Insert: override 快捷键重写
  • private修饰的方法,表示私有的,不能被重写
  • final修饰的方法,表示常量,不能被重写
  • 补充:子类中不能定义一个和父类方法名相同,但返回值类型不同的方法

注意

  • 重写都是方法的重写,和属性无关
  • 私有方法不能继承,所以不能重写
  • 构造方法不能继承,所以不能重写
  • 静态方法不存在重写,(static关键字)
public class Animal {
    public void move(){
        System.out.println("动物在移动");
    }
}
---------------------------------------------------------------------------------
public class Cat extends Animal{
    public void move(){
        System.out.println("猫在走猫步");
    }
}
---------------------------------------------------------------------------------
    public class Demo {
    public static void main(String[] args) {
        Animal c = new Cat();
        c.move();
    }
}

最后的结果是"猫在走猫步"

这里就是子类重写了父类的方法,单独这么看起来有点蠢,为了达到相同的效果甚至完全不用什么继承什么重写,是设想一个父类有几万行代码,几百个方法,这个时候子类继承父类,再通过重写就能满足子类的业务需求

多态

  • 动态编译:使类型可扩展性更强
  • 简而言之就是同一个行为具有多个不同表现形式或形态的能力 有一杯水,不知道它是温的、冰的还是烫的,但是一摸就知道了 摸水杯这个动作,对于不同温度的水,就会得到不同的结果,这就是多态
  • 同一方法可以根据发送对象的不同而采用多种不同的行为方式
  • 一个对象的实际类型是确定的,但可以指向对象的引用的类型有很多
  • 多态存在的条件
    • 有继承关系
    • 子类重写父类方法
    • 父类引用指向子类对象
  • 注意:多态是方法的多态,属性没有多态性

向上转型(upcasting)

  • 必须要有继承关系

  • 父类引用指向了**子类对象 **

    • Animal a = new Cat();
    • a是引用 子类对象是Cat类型
    • 猫显然是动物,等号的右边先执行,右边执行结束的结果赋值给左边的变量
  • 相当于基本类型里的低到高

  • 程序分为编译期和运行期

    • 当处在编译阶段时,编译器只认a是一个Animal类型 (静态绑定)
      在这里插入图片描述

    • 可以看到并没有报错,编译器此时只认a是一个Animal类型,如果我们访问a.catchMouse
      * [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-7aOSl0Jw-1613657712685)(C:\Users\10272\AppData\Roaming\Typora\typora-user-images\image-20210218035416076.png)]

    • 结果就发生了编译错误,从图中的错误提示我们可以看到,编译器此时认准了a是一个Animal类型(静态绑定)

    • 而在运行阶段,看的是底层真实运行的对象,也就是Cat,底层是Cat,自然就会去调用Cat的方法
      * [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-IC7N6HTP-1613657712688)(C:\Users\10272\AppData\Roaming\Typora\typora-user-images\image-20210218035931555.png)]

    • 这里的结果是"动物在移动",因为子类Cat里没有move方法,而是继承了父类的方法,如果给Cat类加上move方法,构成重写
      在这里插入图片描述

    • 通过重写,我们最终调用了子类里的方法

向下转型(downcasting)

  • 必须有继承关系
  • 如果我们想让Animal类型的a执行catMouse方法,我们可以将Animal类型的a强制转换成Cat类型
    • 即父类转换为子类,从高到低,强制类型转换,需要加强制类型转换符
  • 什么时候需要使用向下转型呢?
    • 当调用的方法是子类中所特有的,在父类当中不存在时
  • 在a前面加上Cat类型,(Cat)a,由于是高转低,所以要加上括号
    在这里插入图片描述

多态的深层理解

此时我们再定义一个Bird类继承Animal类,做这样的操作:

Animal a = new Bird();
Cat c = (Cat)a;

发现并没有报错,语法合格,这是因为:

  • 编译器检查到a的类型时Animal
  • Animal和Cat之间存在继承关系,并且Animal是父类,Cat是子类
  • Animal向下转型成Cat类型

我们调用Cat里面的catchMouse方法,发现编译器也没有报错

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-bOAGzn6s-1613657712697)(C:\Users\10272\AppData\Roaming\Typora\typora-user-images\image-20210218174527971.png)]

但是当我们运行程序时,出现了报错

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-tQsi3qWp-1613657712699)(C:\Users\10272\AppData\Roaming\Typora\typora-user-images\image-20210218174601819.png)]

出现异常:ClassCastException,显示Bird cannot be cast

更加验证了:

  • JVM堆内存当中真实存在的是Bird类型,Bird类型无法转换成Cat对象,因为两种类型之间不存在任何继承关系

这种异常总是在向下转型时发生,也就是说向下转型存在隐患(编译过了,但是运行失败了)

向上转型只要编译通过,运行就不会出问题

为了避免出现以上异常,我们使用Java提供了instanceof运算符

instanceof运算符

  • 语法格式:引用 instanceof 数据类型名
  • 返回值为布尔值
  • a instanceof Animal
    • true表示:
      • a这个引用指向的对象是一个Animal类型
    • false表示:
      • a这个引用指向的对象不是一个Animal类型

上面的Cat c = (Cat)a;语句,只有当a引用指向的对象确实是一个Cat的时候才成立

因此在执行Cat c = (Cat)a;语句之前,我们应该先用instanceof判断

if(a instanceof Cat){
    Cat c = (Cat)a;
}

鸟类中有特有的方法,我们就可以这么写

if(a instanceof Cat){
    Cat c = (Cat)a;
    c.catchMouse();
}else if(a3 instance of Bird){
    Bird b = (Bird)a;
    b.fly();
}
  • 在进行强制类型转换之前,建议采用instanceof运算符进行判断,避免ClassCastException异常的发生,这是一种编程的好习惯

多态的作用

  • 降低程序的耦合度,提高程序的扩展力

以人喂养宠物为例

  • 当不使用多态语法的时候

    • 首先根据面向对象思想,我们把主人和小猫都看成是对象

    • 其次我们在测试类里面new出主人和小猫对象

    • 在主人类里面写出喂养的方法

    • 在小猫类里面写出吃鱼的方法

    • 最后在测试类里面调用

    • public class Master {
          public void feed(Cat c){
              c.eat();
          }
      }
      --------------------------------------------------------------------------
          
      public class Cat extends Animal {
          public void eat(){
              System.out.println("小猫在吃鱼");
          }
      }
      --------------------------------------------------------------------------
      
      public class Demo {
          public static void main(String[] args) {
              Master ycy = new Master();
              Cat tom = new Cat();
              ycy.feed(tom);
          }
      }
      --------------------------------------------------------------------------
          //输出的结果是 小猫在吃鱼
      
    • 如果主人又养了小狗,(客户或者需求的变化,意味着软件需要升级)

    • 我们需要在主人类里面再加上小狗吃骨头的方法

    • 在测试类里把小狗new出来,创建小狗对象

    • public class Dog {
          public void eat(){
              System.out.println("小狗吃骨头");
          }
      }
      --------------------------------------------------------------------------
      public class Master {
          public void feed(Cat c){
              c.eat();
          }
          public void feed(Dog d){
              d.eat();
          }
      }
      --------------------------------------------------------------------------
       public class Demo {
          public static void main(String[] args) {
              Master ycy = new Master();
              Cat tom = new Cat();
              ycy.feed(tom);
              Dog Wangcai = new Dog();
              ycy.feed(Wangcai);
          }
      }
      --------------------------------------------------------------------------
      
    • 不使用多态机制,我们发现主人类的扩展性太差,每次增加一个宠物,都要在主人类新写一个方法,在真正的实战项目中,可能一下子增加的就是几百个

  • 使用多态语法,降低程序的耦合度,提高程序的扩展力

    • 首先我们再增加一个Pet类,作为小猫和小狗的父类,在里面写一个eat方法,方法体什么都不用写,我们通过override来实现子类的方法

    • public class Pet {
          public void eat(){
          }
      }
      --------------------------------------------------------------------------
      public class Master {
          public void feed(Pet x){
              x.eat();
          }
      }
      --------------------------------------------------------------------------
      
    • 现在主人类不需要面向小猫或者小狗,只需要面向Pet类就可以了

    • 测试类有两种写法

    • public class Pet {
          public void eat(){
          }
      }
      --------------------------------------------------------------------------
      public class Master {
          public void feed(Pet x){
              x.eat();
          }
      }
      --------------------------------------------------------------------------
      public class Demo {  //写法一
          public static void main(String[] args) {
              Master ycy = new Master();
              ycy.feed(new Cat());
          }
      }
      --------------------------------------------------------------------------
      public class Demo {  //写法二
          public static void main(String[] args) {
              Master ycy = new Master();
              Cat c = new Cat();
              ycy.feed(c);
          }
      } 
      --------------------------------------------------------------------------
      
    • 最后结果都是"小猫在吃鱼"

对于使用了多态的写法,如果主人又要喂养什么新的宠物,那么在主人类就不需要再去编写方法了,著人类面向的是一个抽象的Pet耦合度非常低

  • 核心:面向抽象编程,尽量不要面向具体编程
  • 5
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 5
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值