JavaSE:多态

向上转型:

先看一段代码:

 为何Animal animal=new Dog这个代码不报错。就是因为使用了向上转型:父类引用引用子类对象

向上转型一共有三种方式可以实现向上转型:1.直接赋值,2.通过传参,3.返回值

1.直接赋值:Animal animal=new Dog

2.通过传参:

通过func1的参数进行向上转型。

3.返回值:

通过func2的返回值来向上转型。

注意事项:

从上面代码中可以看出,第二个对象animal进行了向上转型,向上转型之后,animal就不能调用子类bark的方法。

对于进行了向上转型的对象,通过父类的引用,调用子类特有的方法,是无法直接调用的,这里只能调用父类自己有的。

所以向上转型的缺点:不能调用子类特有的方法。

重写:

当父类和子类的两个方法:

1.方法名相同

2.参数列表相同(个数,类型,顺序)

3.返回值也相同

就说明这两个方法构成了重写。

重写的两个方法对代码有什么影响呢?请看下面这个代码:

父类的eat方法和子类的eat方法构成了重写

 

然后我们看结果:

运行结果:

重写的注意事项:

1.不能重写一个静态方法,父类的构造方法也不能被重写

2.被final修饰的方法不能被重写,这个方法被称为密封方法

public final void fun(){
    System.out.println(this.name+"正在做作业");
}

3.访问权限的问题,如果子类重写父类的方法,则子类的成员访问修饰符的权限要大于等于父类的成员访问修饰符的权限。

访问权限大小:

private<default<protected<public

 4.如果方法是由private修饰的,则不能被重写。

private void eat(){
    System.out.println("正在吃饭");
}

5.被重写的方法返回值可以不同,但必须构成父子类关系,在重写方法时,子类的返回值类型必须与父类返回值类型相同或者是其子类

//Animal类的方法
public Animal eat(){
        System.out.println(this.name+"正在吃饭...");
        return null;
    }
//Dog重写父类的eat方法
 public Dog eat(){
       System.out.println(this.name+"正在吃狗粮...");
       return null;
   }

动态绑定:

上面构成重写代码中,通过反汇编来看,程序在编译的时候,程序确实调用的是父类的eat方法但是在运行的时候,通过父类的引用,调用了父类和子类重写的那个方法,结果实际调用了子类的方法,此时我们把这个情况叫做动态绑定

动态绑定是理解 多态 的基础。

发生动态绑定的前提是:(大前提:就是一定要在继承的情况下)

1.父类引用引用子类对象(向上转型)

2.通过父类引用,调用了父类和子类重写的那个方法 

 静态绑定:

静态绑定:在编译时,根据用户所传递实参类型就确定了具体调用那个方法,典型代表函数重载

重载:

方法名相同,方法参数不同(参数个数,类型,顺序)

方法返回值类型可以相同可以不同

方法的修饰符可以相同可以不同

 向下转型:

将一个子类对象经过向上转型之后当成父类方法使用,再无法调用子类的方法,但有时候又需要调用子类所特有的方法,此时:将父类引用转化为子类对象,称为向下转型

//Animal类
public class Animal {
    public String name;
    public int age;
    public Animal(String name,int age){
        this.name=name;
        this.age=age;
    }
    public void eat(){
        System.out.println(this.name+"正在吃饭...");
    }
}
//Dog类
public class Dog extends Animal {
   public Dog(String name,int age){
       super(name,age);
   }
   public void bark(){
       System.out.println(this.name+"正在狗叫...");
   }
   public void eat(){
       System.out.println(this.name+"正在吃狗粮...");
   }
}
//鸟类
public class Bird extends Animal{
    public Bird(String name,int age){
        super(name,age);
    }
    public void fly(){
        System.out.println(this.name+"正在飞...");
    }
   public void eat(){
     System.out.println(this.name+"正在吃鸟粮...");
    }
}

//测试类
 class Test {
  public static void main(String[] args) {
     Animal animal1=new Dog("旺财",10);//先向上转型
     Dog dog=(Dog)animal1;//向下转型,可以使用子类的方法
     dog.bark();//使用子类特有的方法
  }
 }

 此时就完成了向下转型。

但是并不是所有的向下转型都能成功:
将上面代码的测试类修改为:

class Test {
  public static void main(String[] args) {
     Animal animal1=new Dog("旺财",10);
     Bird bird=(Bird)animal1;
     bird.fly();
  }
 }

 编译的时候不会提醒你有错,但是运行时就会报错

向下转型用的少,而且不安全,万一转换失败就会抛异常,所以java为了提高向下转型的安全性,引入了instanceof,如果表达式为true,则可以成功转换

instanceof运算符:

instanceof用法:

对象 instanceof

 对象是要检查的对象,类是要检查的类,instanceof运算符会返回一个布尔值,表示对象是否属于该类或者其子类,如果属于就返回true,反之则返回false。

再次修改上面的测试类:

 class Test {
  public static void main(String[] args) {
     Animal animal1=new Dog("旺财",10);
     if(animal1 instanceof Bird){
         Bird bird=(Bird)animal1;
        bird.fly();
     }else{
         System.out.println("animal1 instanceof Bird not!!!");
     }
  }
 }

 animal1不属于Bird类。也不属于Bird的子类,所以不能安全转换。

 这样就能使代码更加安全,如果animal1属于Bird类或者属于其子类就可以安全向下转型

多态的概念:

通俗来说,就是多种形态,具体点就是去完成某个行为,当不同的对象去完成时会产生出不同的形态。

先看代码:

//Animal类
public class Animal {
    public String name;
    public int age;
    public Animal(String name,int age){
        this.name=name;
        this.age=age;
    }
    public void eat(){
        System.out.println(this.name+"正在吃饭...");
    }
}
//Bird类
public class Bird extends Animal{
    public Bird(String name,int age){
        super(name,age);
    }
    public void fly(){
        System.out.println(this.name+"正在飞...");
    }
    public void eat(){
        System.out.println(this.name+"正在吃鸟粮...");
    }
}
//Dog类
public class Dog extends Animal {
   public Dog(String name,int age){
       super(name,age);
   }
   public void bark(){
       System.out.println(this.name+"正在狗叫...");
   }
   public void eat(){
       System.out.println(this.name+"正在吃狗粮...");
   }
}
测试类
class Test {
    public static void func(Animal animal){
        animal.eat();
    }
     public static void main(String[] args) {
        Dog dog=new Dog("旺财",19);
         func(dog);
         System.out.println("===========");
         Bird bird=new Bird("小鸟",12);
         func(bird);
     }
 }

运行结果:

 这里调用了两次相同的func方法,但是结果不一样

调用相同的方法,不同的对象去调用产生的结果不一样,这就是多态。

多态实现的条件:

1.必须在继承体系下

2.子类必须要对父类的方法进行重写

3.通过父类的引用调用重写的方法

 多态的优缺点:

 先给几个类:

//画一个图形的类
public class Shape {
    public void draw() {
        System.out.println("画一个图像");
    }
}
//画一个圆形的类
public class Cycle extends Shape{
    public void draw(){
        System.out.println("画一个○");
    }
}
//画一个三角形的类
public class Triangle extends Shape{
    public void draw(){
        System.out.println("画一个△");
    }
}
//画一个正方形的类
public class Rect extends Shape {
    public void draw() {
        System.out.println("画一个□");
    }
}

多态的优点:

1.可以降低代码的“圈复杂度”,避免使用大量的if-else语句。

圈复杂度:圈复杂度是一种描述一段代码复杂程度的方式,一段代码如果平铺直叙,那这段代码就更容易理解,而如果有很多的条件分支和循环语句,那么认为代码就更难理解,更复杂。

在上面打印图形代码中,如果我们要打印多个图形,且不用多态,代码如下:

//测试类
class Test {
  public static void main(String[] args) {
   Cycle cycle=new Cycle();
   Rect rect=new Rect();
   Triangle triangle=new Triangle();
   String[] shape={"Cycle","Rect","Triangle"};
   for (int i = 0; i < shape.length; i++) {
    if(shape[i].equals("Cycle")){
     cycle.draw();
    }else if(shape[i].equals("Rect")){
     rect.draw();
    }else {
     triangle.draw();
    }
   }
  }
 }

但是如果使用多态,就不用写这么多的if-else,从而降低圈复杂度:

class Test {
  public static void main(String[] args) {
//创建一个Shape对象的数组
   Shape[] shape={new Cycle(),new Rect(),new Triangle()};
   for (Shape x: shape) {
    x.draw();
   }
  }
 }

 打印结果:

 2.扩展能力强:

如果要新填一种形式,使用多态成本也比较低,只需要调用者创建一个新类就可以了,比如添加画一个花的类。

public class Flower extends Shape{
    public void draw() {
        System.out.println("画一个❀");
    }
}

创建一个花类后,然后修改Shape对象数组中的参数

class Test {
  public static void main(String[] args) {
   Shape[] shape={new Cycle(),new Rect(),new Triangle(),new Flower()};
   for (Shape x: shape) {
    x.draw();
   }
  }
 }

打印结果:

但是如果不用多态,就要增加if-else分支语句,从而使得圈复杂度更高。

 多态的缺点:

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

2.构造方法没有多态性。

 避免在构造方法中调用重写方法:

B是父类,D是子类,D重写B的func方法,并在B的func方法中调用func方法。

public class B {
    public B() {
        func();
    }
    public void func(){
        System.out.println("func-父类B");
    }
}
public class D extends B{
    public int num=1;
    public void func(){
        System.out.println("func-子类"+num);
    }
}
public class Test {
    public static void main(String[] args) {
        D d=new D();
    }
}

执行结果:

func-子类0

 为什么最后的num等于0呢?

之前讲过静态代码块,实例代码块,构造方法的执行顺序。(可以去看)

这里在创建d的同时,会调用B的构造方法,因为默认在子类D中提供了构造方法。

B的构造方法在调用func的方法时,会触发动态绑定,然后会调用子类的重写的func方法

D方法中没有构造方法,此时num处于未初始化状态,所以默认为0,所以在构造函数内,尽量不要调用实例方法,除了final和private方法。

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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/754453.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

virtualbox安装win10

等到安装完成 设备下选择安装增强功能

【教程】几种不同的RBF神经网络

本站原创文章&#xff0c;转载请说明来自《老饼讲解-机器学习》www.bbbdata.com 目录 一、经典RBF神经网络1.1.经典径向基神经网络是什么1.2.经典径向基神经网络-代码与示例 二、广义回归神经网络GRNN2.1.广义回归神经网络是什么2.2.广义回归神经网络是什么-代码与示例 三、概率…

Redis 5 种基础数据结构?

Redis 5 种基本数据结构(String、List、Hash、Set、Sorted Set)在面试中经常会被问到&#xff0c;这篇文章我们一起来回顾温习一下。 还有几种比较特殊的数据结构(HyperLogLogs、Bitmap 、Geospatial、Stream)也非常重要&#xff0c;我们后面下次再聊&#xff01; 下面是正文。…

双减期末考试成绩怎么公布?

考试一直是衡量学生学习成果的重要手段。不过&#xff0c;随着"双减"政策的实施&#xff0c;我们就不得不重新审视传统的成绩公布方式。期末考试成绩&#xff0c;这个曾经让无数学生心跳加速的数字&#xff0c;如今该如何以一种更加合理、公正的方式呈现给学生和家长…

广和通 OpenCPU 二次开发(一) —— 串口

广和通 OpenCPU 二次开发&#xff08;一&#xff09; —— 串口 1.port&#xff0c;端口号2.引脚序列号对应芯片引脚图找&#xff0c;也可以对照GPIO功能复用表找3.要复用的pin脚对应的功能mode根据GPIO功能复用表选择 一、核心配置## 标题代码 int port 1; fibo_gpio_mode_s…

力扣SQL50 员工的直属部门 子查询 双重

Problem: 1789. 员工的直属部门 &#x1f468;‍&#x1f3eb; 参考题解 Code select employee_id, department_id from Employee where primary_flag Y # Y 表明是直属部门 or employee_id in (select employee_idfrom Employeegroup by employee_idhaving count(employee…

国外的Claude3.5 Sonnet Artifacts和国内的CodeFlying孰强孰弱?

在Claude 3.5 Sonnet发布后&#xff0c;最受大家关注的问题应该就是它在编写代码能力上的变化。 要知道在Claude3.0发布以来的这几个月就因为它的编写代码能力而一直受到人们的诟病。 那Anthropic这次终于是不负众望&#xff0c;在Claude 3.5 Sonnet中更新了一个叫做Artifact…

ETAS工具导入DEXT生成Dcm及Dem模块(一)

文章目录 前言Cfggen之前的修改ECU关联DcmDslConnectionDiagnostic ProtocolDiagnostic Ecu Instance PropsCommonContributionSetEvent修改communication channel总结前言 诊断模块开发一般是先设计诊断数据库,OEM会释放对应的诊断数据库,如.odx文件或.cdd文件。如果OEM没有…

go~缓存设计配合singleFlight

一个缓存设计&#xff0c;配合go的singleFlight 最开始的设计如下 添加分布式缓存 上线后分布式缓存上涨的流量并不等于下游下降的流量&#xff0c;而是下游下降的流量 * 2&#xff5e;3 究其原因&#xff0c;就是采用了go的singleFlight&#xff0c;假定请求缓存时长10ms&a…

LabVIEW网络开发资源

在LabVIEW开发中&#xff0c;利用网络资源进行学习和查找资料是提高技能和解决问题的重要途径。以下几个国内外优质资源可以帮助开发者获得丰富的技术支持和交流机会&#xff1a; 1. NI Community (NI社区) 简介: National Instruments官方运营的社区&#xff0c;提供丰富的资…

浅谈:冒烟测试

在软件开发的生命周期中&#xff0c;测试阶段是确保产品质量的关键环节。冒烟测试作为软件测试的一种快速而有效的初步验证方法&#xff0c;重要性不言而喻。 冒烟测试源自制造业&#xff0c;尤其是电子行业。当一块电路板被制造出来后&#xff0c;工程师们会首次通电&#xf…

【应用开发二】GPIO操控(输出、输入、中断)

1 操控GPIO方式 控制目录&#xff1a;/sys/class/gpio /sys/class/gpio目录下文件如下图所示&#xff1a; 1.1 gpiochipX目录 功能&#xff1a;当前SoC所包含的所有GPIO控制器 i.mx6ull一共包含5个GPIO控制器&#xff0c;分别为GPIO1~5分别对应gpiochip0、gpiochip32、gpi…

【漏洞复现】用友 GRP-U8 FileUpload 任意文件上传漏洞

免责声明&#xff1a; 本文内容旨在提供有关特定漏洞或安全漏洞的信息&#xff0c;以帮助用户更好地了解可能存在的风险。公布此类信息的目的在于促进网络安全意识和技术进步&#xff0c;并非出于任何恶意目的。阅读者应该明白&#xff0c;在利用本文提到的漏洞信息或进行相关测…

蒙特卡洛法求定积分方

对于连续函数密度函数&#xff0c;求某一个区间的概率时&#xff0c;理论上通过积分获取&#xff0c; 以求曲线围成的面积为例 当我们在[a,b]之间随机取一点x时&#xff0c;它对应的函数值就是f(x)。接下来我们就可以用f(x)*(b-a)来粗略估计曲线下方的面积&#xff0c;也就是我…

Redis主从复制、哨兵以及Cluster集群

1.Redis高可用 在web服务器中&#xff0c;高可用是指服务器可以正常访问的时间&#xff0c;衡量的标准是在多长时间内可以提供正常服务&#xff08;99.9%、99.99%、99.999%等等&#xff09;。 但是在Redis语境中&#xff0c;高可用的含义似乎要宽泛一些&#xff0c;除了保证提供…

Linux 异步 I/O 框架 io_uring:基本原理、程序示例与性能压测

Linux 异步 I/O 框架 io_uring 前言Linux I/O 系统调用演进io_uring与 Linux AIO 的不同原理及核心数据结构&#xff1a;SQ/CQ/SQE/CQE带来的好处三种工作模式io_uring 系统调用 API 前言 io_uring 是 2019 年 Linux 5.1 内核首次引入的高性能 异步 I/O 框架&#xff0c;能显著…

003-GeoGebra如何无缝嵌入到PPT里

GeoGebra无缝嵌入到PPT里真是一个头疼的问题&#xff0c;已成功解决&#xff0c;这里记录一下&#xff0c;希望可以帮助到更多人。 注意&#xff0c;后续所有的文章说的PPT都是Offce Power Point, 不要拿着WPS的bug来问我哦&#xff0c;我已经戒WPS了&#xff08;此处表示无奈&…

typescript学习回顾(四)

今天来分享下ts中的类&#xff0c;关于ts中的类的概念&#xff0c;面向对象的一种思想&#xff0c;以及类里面的一些属性成员&#xff0c;一些基础的用法&#xff0c;后面会有一个小练习。 类 基本概念 我的理解&#xff1a;类是编程语言中面向对象的一种思想&#xff0c;一…

人脑计算机技术与Neuroplatform:未来计算的革命性进展

引言 想象一下&#xff0c;你在某个清晨醒来&#xff0c;准备开始一天的工作&#xff0c;而实际上你的大脑正作为一台生物计算机的核心&#xff0c;处理着大量复杂的信息。这并非科幻电影的情节&#xff0c;而是人脑计算机技术即将带来的现实。本文将深入探讨FinalSpark公司的…

Anisble Playbook

文章目录 一、Playbook简介三种常见的数据格式Playbook特点YAML语言介绍 二、Playbook核心组件host组件remote_user组件task列表和action组件gather_factsHandlers notifyignore_errors 三、playbook命令playbook命令tags 标签 四、Playbook中的变量setup模块中的变量Playbook命…