当前位置: 首页 > news >正文

Java之多态

文章目录

  • 1. 多态
    • 1.1 多态的概念
  • 2. 方法的重写
  • 3. 向上转型
    • 3.1
    • 3.2 发生向上转型的时机
  • 4. 动态绑定和静态绑定
  • 5. 什么是多态
    • 5.1
    • 5.2 多态的优缺点
  • 6. 避免在构造方法中调用重写的方法
  • 7. 向下转型![在这里插入图片描述](https://i-blog.csdnimg.cn/direct/fd1fa83140d94f37ab3b881b0df41305.png)

1. 多态

1.1 多态的概念

去完成某个行为,不同的对象去完成会产生不同的状态。例如,猫和狗都在叫,而猫的叫的是喵喵,狗叫的是汪汪。

在讲解多态之前我们先学习什么是向上转型向下转型方法重写

2. 方法的重写

重写(override),也称为覆盖。重写是子类对父类非静态、非private修饰,非final修饰,非构造方法等的实现过程进行重新编写。

构成方法重写的条件

  1. 方法名相同
  2. 方法的参数列表相同(个数、顺序、类型)
  3. 方法的返回值相同

例如:
在这里插入图片描述
上述两个 bark 方法,实现了方法的重写。重写的方法通常会有 @Override 来修饰,表示方法的重写,可以用来提示重写的方法是否出现错误,如果出现错误@Override处会报错。
在这里插入图片描述
注意

  1. 静态方法不能被重写
  2. 被private修饰的方法不能被重写
  3. 被final修饰的方法不能被重写
  4. 如果方法被重写,子类的访问权限要大于等于父类的访问权限
    private < 包访问权限 < protected < public

跟重载是有区别的,注意区分哦
在这里插入图片描述

3. 向上转型

3.1

在这里插入图片描述
语法格式:父类类型 对象名 = new 子类类型()

public class Animal {public String name;public int age;public Animal(){}public Animal(String name,int age){this.name = name;this.age = age;}public void eat(){System.out.println(this.name + "正在吃饭");}public void bark(){System.out.println(this.name + "正在叫");}
}public class Cat extends Animal{public Cat(){}public Cat(String name,int age){super(name,age);}public void bark(){System.out.println(this.name + "正在喵喵叫");}
}public class Dog extends Animal{public Dog(){}public Dog(String name,int age){super(name, age);}@Overridepublic void bark() {System.out.println(this.name + "正在汪汪叫");}
}public class Main {public static void main(String[] args) {Animal animal = new Dog();//实现了向上转型animal.bark();}
}

观察上述代码,Cat 类和 Dog 类中都重写了 bark 方法,肯定有人觉得main 方法中的 bark 方法可有可无,当我们删除这个 bark 方法时,animal无法访问bark这个方法。
在这里插入图片描述
无法访问的原因是在Animal当中没有该方法,通过父类引用访问的时候,只能访问父类自己特有的,所以我们不能删除。

是不是在等向下转型,因为向下转型不常用,所以放在最后面了
在这里插入图片描述

3.2 发生向上转型的时机

  1. 直接赋值
public class Main {public static void main(String[] args) {Animal animal = new Cat("zaizai",2);}
}
  1. 方法传参的时候
public class Main {public static void func(Animal animal){}public static void main(String[] args) {func(new Cat());}
}
  1. 返回值的时候
public class Main {public static Animal func(){return new Cat() ;}public static void main(String[] args) {func();}
}

不是只有直接赋值一种哦,要注意啦
在这里插入图片描述

向上转型的优点:让代码的实现更加简单
向上转型的缺点:无法调用到子类特有的方法

4. 动态绑定和静态绑定

当父类引用子类的对象(即向上转型)时,通过父类引用、调用重写的方法就实现了动态绑定

动态绑定:也称为后期绑定(晚绑定),即在编译时,不能确定方法的行为,需要等到程序运行时,才能够确定具体调用那个类的方法。

静态绑定:也称为前期绑定(早绑定),即在编译时,根据用户所传递实参类型就确定了具体调用那个方法。典型代表函数重载。
在这里插入图片描述

5. 什么是多态

5.1

回归正题,什么是多态呢
在这里插入图片描述
其实,上述代码就发生了多态,当 animal 引用的对象不一样,调用 eat 方法,表现的行为不一样,此时就叫多态。(同一个引用调用同一个方法表现的行为不一样

5.2 多态的优缺点

优点

  1. 降低圈复杂度(是一种用于评估代码复杂性的软件度量方法,代码的分支 / 判断越多,圈复杂度越高),避免使用大量的 if-else

例如:

public class Shape {public void draw(){System.out.println("画一个图形");}
}public class Rectangle extends Shape{@Overridepublic void draw() {System.out.println("画一个矩形");}
}public class Triangle extends Shape{@Overridepublic void draw() {System.out.println("画一个三角形");}
}public class Circle extends Shape {@Overridepublic void draw() {System.out.println("画一个圆");}
}public class Main {public static void drawMap(Shape shape){shape.draw();}public static void main(String[] args) {drawMap(new Triangle());drawMap(new Rectangle());drawMap(new Circle());}
}

例如,我们现在需要打印多个形状. 如果不基于多态, 实现代码如下

public static void drawMap() {Rectangle rectangle = new Rectangle();Triangle triangle = new Triangle();Circle circle = new Circle();String[] shapes = {"Rectangle","Circle","Circle","Triangle","Rectangle"};for(String shape:shapes){if(shape.equals("Rectangle"))rectangle.draw();else if(shape.equals("Circle"))circle.draw();else if(shape.equals("Triangle"))triangle.draw();}
}

基于多态

public static void drawMap(){Shape Rectangle = new Rectangle();Shape Triangle = new Triangle();Shape Circle = new Circle();Shape[] shapes = {Rectangle,Circle,Circle,Triangle,Rectangle};for(Shape shape:shapes){shape.draw();}}

果然基于多态方便了许多,没白学
在这里插入图片描述

不基于多态,圈复杂度高。基于多态,圈复杂度低,代码更简单。

  1. 可扩展性强
    当我们新增一个图形,创建一个新的实例就可以,改动代码较少。

缺点

  1. 属性没有多态性
    当父类和子类都有同名属性的时候,通过父类引用,只能引用父类自己的成员属性

  2. 构造方法没有多态性

6. 避免在构造方法中调用重写的方法

当我们在构造方法中调用重写的方法的代码如下:

public class B {public B(){func();}public void func(){System.out.println("B:func()");}
}class D extends B{private int num = 1;public void func(){System.out.println("D:func()" + num);}
}class Main{public static void main(String[] args) {D d = new D();}
}//运行结果
D:func()0

为什么会调用 D 的 func 方法呢?而且 num = 0 ,让我们来看一下它具体是怎么执行的
在这里插入图片描述

鸡爪子画图,宣!
在这里插入图片描述

步骤

  1. 构造D对象的同时,调用B的无参构造方法。
  2. B的构造方法中调用了 func 方法,此时触发动态绑定,调用 D 的 func 方法。
  3. 此时,D 对象自身还未构造,num 处于未初始化的状态,所以 num = 0

结论:尽量不要在构造器中调用方法(如果这个方法被子类重写, 就会触发动态绑定, 但是此时子类对象还没构造完成), 可能会出现一些隐藏的但是又极难发现的问题。

7. 向下转型在这里插入图片描述

语法格式:子类类型 对象名 = (子类类型)new 父类类型()
需要强转

例如:

Dog dog = (Dog)new Animal();

那么不强转为什么会报错呢?
我们可以理解为不是所有的动物都是狗,所以会报错。

但是向下转型非常不安全,例如

 Animal animal = new Cat();Dog dog = (Dog)new Animal();dog.bark();

虽然代码在编译层次上不会报错,但是运行结果为类型转换异常
在这里插入图片描述

Animal animal = new Cat();
Cat cat = (Cat)animal;
cat.bark();

因为animal引用的本身就是Cat对象,所以不会报错。

Java中为了提高向下转型的安全性,引入了 instanceof ,如果该表达式为true,则可以安全转换。

Animal animal = new Cat();
if(animal instanceof Cat){Cat cat = (Cat)animal;cat.bark();
}

上述语句表达的意思是,如果 animal 引用的是 Cat 类型……

虽然向下转型不安全,但是可以使用上述语句来解决问题。

终于完事了,我要奖励自己一下了
在这里插入图片描述

相关文章:

  • 关于贪心算法
  • 【系统交付资料】软件文档交付清单整理套用原件(Word,PPT,Excel)
  • 企业如何保护自身通信渠道被黑客攻击
  • 【蚂蚁HR-注册/登录安全分析报告】
  • kubernetes存储入门(kubernetes)
  • 鸿蒙面试题库收集(一):ArkTSArkUI-基础理论
  • 支付宝远程收款api之小荷包跳转码
  • 【算法——KMP】
  • Spring Boot 整合 Keycloak
  • K8s flink-operator 例子
  • 多无人机通信(多机通信)+配置ssh服务
  • opengauss使用遇到的问题,随时更新
  • react是一种语言?
  • 高效编程的利器 Jupyter Notebook
  • (undone) MIT6.824 Lecture1 笔记
  • 【EOS】Cleos基础
  • leetcode讲解--894. All Possible Full Binary Trees
  • Mysql数据库的条件查询语句
  • nginx(二):进阶配置介绍--rewrite用法,压缩,https虚拟主机等
  • Quartz实现数据同步 | 从0开始构建SpringCloud微服务(3)
  • Spark in action on Kubernetes - Playground搭建与架构浅析
  • vue2.0项目引入element-ui
  • 道格拉斯-普克 抽稀算法 附javascript实现
  • 关键词挖掘技术哪家强(一)基于node.js技术开发一个关键字查询工具
  • 机器学习中为什么要做归一化normalization
  • linux 淘宝开源监控工具tsar
  • PostgreSQL 快速给指定表每个字段创建索引 - 1
  • ​ 全球云科技基础设施:亚马逊云科技的海外服务器网络如何演进
  • ​香农与信息论三大定律
  • ​直流电和交流电有什么区别为什么这个时候又要变成直流电呢?交流转换到直流(整流器)直流变交流(逆变器)​
  • %@ page import=%的用法
  • (2022版)一套教程搞定k8s安装到实战 | RBAC
  • (C语言)fgets与fputs函数详解
  • (LeetCode 49)Anagrams
  • (动手学习深度学习)第13章 计算机视觉---图像增广与微调
  • (转载)利用webkit抓取动态网页和链接
  • (轉貼)《OOD启思录》:61条面向对象设计的经验原则 (OO)
  • .FileZilla的使用和主动模式被动模式介绍
  • .MSSQLSERVER 导入导出 命令集--堪称经典,值得借鉴!
  • .NET 8.0 发布到 IIS
  • .NET 漏洞分析 | 某ERP系统存在SQL注入
  • .net 使用ajax控件后如何调用前端脚本
  • .NET开源项目介绍及资源推荐:数据持久层 (微软MVP写作)
  • @Bean有哪些属性
  • @Pointcut 使用
  • @vue-office/excel 解决移动端预览excel文件触发软键盘
  • [100天算法】-不同路径 III(day 73)
  • [14]内置对象
  • [145] 二叉树的后序遍历 js
  • [20170728]oracle保留字.txt
  • [20190401]关于semtimedop函数调用.txt
  • [AutoSar]状态管理(五)Dcm与BswM、EcuM的复位实现
  • [C#]winform基于opencvsharp结合Diffusion-Low-Light算法实现低光图像增强黑暗图片变亮变清晰
  • [C#基础]说说lock到底锁谁?
  • [C/C++]数据结构 循环队列