Java多态

2023-11-17

  1. 关于引用的进一步理解(交换值)
    因为Java方法在传递参数的时候都是值传递,那么如何通过方法实现2个数的值交换?
    明确:在传引用的时候,到底拿引用干了个啥
class Value {
    public int a;
}

public class Test {

    public static void swap(Value value1, Value value2) {
        int tmp = value1.a;
        value1.a = value2.a;
        value2.a = tmp;
    }

    public static void main(String[] args) {
        //交换值,如果只是简单的定义a和b不太能做到,因为他们在栈上,我们要想办法把他们放到堆上
        Value value1 = new Value();
        Value value2 = new Value();
        value1.a = 10;
        value2.a = 20;
        swap(value1, value2);
        System.out.println(value1.a);
        System.out.println(value2.a);
    }

在这里插入图片描述
将属性a的权限改成private,此时在类外a是被封装起来的,不能够直接赋值了,可以提供get和set方法来实现交换值

class Value {
    private int a;

    public int getA() {
        return a;
    }

    public void setA(int a) {
        this.a = a;
    }
}

public class Test {

    public static void swap(Value value1, Value value2) {
//        int tmp = value1.a;
        int tmp = value1.getA();
//        value1.a = value2.a;
        value1.setA(value2.getA());
//        value2.a = tmp;
        value2.setA(tmp);
    }

    public static void main(String[] args) {
        //交换值,如果只是简单的定义a和b不太能做到,因为他们在栈上,我们要想办法把他们放到堆上
        Value value1 = new Value();
        Value value2 = new Value();
        //value1.a = 10;
        value1.setA(10);
        //value2.a = 20;
        value2.setA(20);
        swap(value1, value2);
        System.out.println(value1.getA());
        System.out.println(value2.getA());
    }

    public static void main1(String[] args) {
        Derived derived = new Derived();
        derived.fun();
    }
}

运行结果:
在这里插入图片描述

  1. 什么是多态?
    发生多态的3个条件①在继承的条件下②发生向上转型③方法重写
    多态是一种思想,父类引用引用不同对象的时候,表现出来的行为是不一样的。【这就叫做多态】
    多态的前提是:动态绑定
    【一个父类引用 指向的对象不一样,调用重写的方法,会表现出不同的行为。】

  2. 向上转型
    分别定义动物类、狗类、鸟类

class Aniaml {
    public String name;
    public int age;

    public void eat() {
        System.out.println(name + "吃东西");
    }
}

class Dog extends Aniaml {
    public void wangwnag() {
        System.out.println(name + "汪汪汪");
    }
}

class Bird extends Aniaml {
    public String wing;
    public void miaomiao() {
        System.out.println(name + "喵喵喵");
    }
}

public class Test1 {
    public static void main(String[] args) {
        Dog dog = new Dog();
        dog.name = "小狗";
        dog.eat();
        dog.wangwnag();
        System.out.println("===分割===");
        Bird bird = new Bird();
        bird.name = "小鸟";
        bird.eat();
        bird.miaomiao();
    }
}

运行结果:
在这里插入图片描述
如果修改main方法中的实例化对象语句为:

        Aniaml aniaml1 = new Dog();
        aniaml1.name = "小狗";
        aniaml1.eat();
//        aniaml1.wangwang();  //报错,因为只能访问Aniaml类中有的成员
        System.out.println("===分割===");
        Aniaml aniaml2 = new Bird();
        aniaml2.name = "小鸟";
        aniaml2.eat();

此时 Aniaml aniaml1 = new Dog();,左边是动物类,右边是狗类,发生了向上转型,父类引用指向了子类对象。
理论上:等号两边的数据类型必须一致,否则赋值出错。
当发生向上转型之后,此时通过父类的引用只能访问父类自己的成员,不能访问到子类特有的成员,也就是说,只animal能调用Animal类中的方法和属性,不能再访问Dog类和Bird类子类中独有的方法和属性了。

  1. 向上转型的3种场景
    ①直接赋值
        Aniaml aniaml1 = new Dog();
        aniaml1.name = "小狗";
        aniaml1.eat();

②方法传参

    public static void fun(Aniaml aniaml) {
        aniaml.eat();
    }

    public static void main(String[] args) {
        Dog dog = new Dog("小狗");
        fun(dog);
    }

③方法返回值

    public static Aniaml func() {
        return new Dog();
    }

向上转型的优点:让代码实现更简单灵活。(animal能够引用它,说明它一定是个animal的子类,它一定是一个动物)
向上转型的缺点:不能访问子类特有的方法。【除非发生了动态绑定,可以调用子类重写的方法】(animal只能调用自己特有的属性和方法)

  1. 方法重写的引入
    在上述例子种,狗和鸟都是吃饭,按照常理,狗应该吃狗粮,鸟应该吃鸟粮,此时就应该在狗类和鸟类种重写父类的eat方法。
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    public static void main(String[] args) {
        Aniaml aniaml1 = new Dog("小狗");
        aniaml1.eat();
        Aniaml aniaml2 = new Bird("小鸟");
        aniaml2.eat();
    }

运行结果:
在这里插入图片描述
分析:在这个过程中,【父类不是只能调用父类的方法吗?这里怎么调用了重写的方法呢?】因为这里发生了动态绑定,编译的时候确实调用的是Animal的eat方法,但是在运行的时候发生了动态绑定,调用了重写的方法。

  1. 动态绑定需要满足的3个条件
    ①向上转型②重写③通过父类引用调用这个父类和子类重写的方法
    【动态绑定是多态的基础】
    动态绑定:也称为后期绑定(晚绑定),在编译的时候不能确定方法的行为,需要等到程序运行的时候,才能确定具体调用哪个类的方法
    静态绑定:也称为前期绑定(早绑定),在编译的时候,根据传入的参数,就能够确定调用哪个方法,典型代表方法重载。

  2. 方法重写的注意点
    重写:是子类对父类非private修饰、非static修饰、非final修饰、非构造方法等进行重新编写。【重写的好处:子类可以根据需要,定义特定于自己的行为,子类可以根据需要实现父类的方法。】
    ①被private修饰的方法不能被重写:因为private权限范围只能在当前类内使用
    ②被static修饰的方法不能被重写:因为static属于类方法,如果被重写,则一个属于动物类,一个属于狗类,和对象毫无关系,分别属于各自所在的类。
    ③被final修饰的方法不能被重写:也叫密封方法
    ④构造方法不能被重写:因为子类的构造方法名字不可能和父类的构造方法的名字一样。
    ⑤子类的访问修饰限定权限要大于等于父类的权限
    private<默认<protected<public

  3. 重写和重载的对比
    重写:①方法名相同 ②参数列表相同③返回值类型相同【当返回值类型不同的时候必须是 父子关系
    重载:①方法名相同②参数列表不同③与返回值类型无关
    【重写的参数列表 一定不能修改,重载的参数列表 必须修改】

  4. 向下转型【不安全】
    将一个子类对象经过向上转型之后当成父类方法使用,这也再无法调用子类的方法,但有时候可能需要调用子类特有的方法,此时需要将父类引用再还原为子类对象。
    例如,将Dog和Bird向上转型成Animal后,无法调用Dog中特有的wangwang方法以及Bird中的zhazha方法,此时需要将Animal再次向下转型成Dog和Bird进行调用子类特有的方法。

    public static void main(String[] args) {
        Aniaml aniaml1 = new Dog("小狗");
        Dog dog = (Dog) aniaml1;
        dog.eat();
        Aniaml aniaml2 = new Bird("小鸟");
        Bird bird = (Bird) aniaml2;
        bird.eat();
    }

运行结果:
在这里插入图片描述
分析:animal1向下转型:本来是狗,还原为狗,安全;animal2向下转型:本来是鸟,还原为鸟,安全。
如果本来是狗,向下转型为鸟,则不安全:

    public static void main(String[] args) {
        Aniaml aniaml = new Dog("小狗");
        Bird bird = (Bird) aniaml;
        bird.eat();
    }

编译没有报错,但是在运行的时候出现类型转换异常
在这里插入图片描述
分析:程序可以通过编程,但是运行时抛出异常,本来是狗,现在要强制还原为猫,无法正常还原,不安全。
Java中为了提高向下转型的安全性,引入 instanceof关键字,如果表达式为true,则可以安全转换。

    public static void main(String[] args) {
        Aniaml aniaml = new Dog("小狗");
        if (aniaml instanceof Bird) {
            Bird bird = (Bird) aniaml;
            bird.eat();
        }else {
            Dog dog = (Dog) aniaml;
            dog.eat();
        }
    }

运行结果:
在这里插入图片描述
分析:aniaml instanceof Bird,判断animal是不是引用了Bird对象,如果引用了就可以向下转型。

  1. 多态的优缺点
    例:定义类,完成画图(圆,矩形,花等)
    使用多态完成:
class Shape {
    public void draw() {
        System.out.println("画图形");
    }
}


class Cir extends Shape {
    @Override
    public void draw() {
        System.out.println("画圆");
    }
}

class Rex extends Shape {
    @Override
    public void draw() {
        System.out.println("画矩形");
    }
}

class Flow extends Shape {
    @Override
    public void draw() {
        System.out.println("画花");
    }
}


public class TestDemo {

    public static void func(Shape shape) {
        shape.draw();
    }

    public static void main(String[] args) {
        Cir cir = new Cir();
        Rex rex = new Rex();
        Flow flow = new Flow();
        
        func(cir);
        func(rex);
        func(flow);


    }
}

运行结果:
在这里插入图片描述

圈复杂度:是一种描述一段代码复杂程度的方式,可以简单粗暴的一段代码中条件语句和循环语句出现的个数,这个个数就称为“圈复杂度”。如果一个方法圈复杂度太高,就需要考虑重构,不同公司对于代码的圈复杂度的规范不一样,一般不会超过10。
假如不使用多态,则要使用到if-else语句等进行判断(此时圈复杂度会很高)。

public static void main(String[] args) {
        Cir cir = new Cir();
        Rex rex = new Rex();
        Flow flow = new Flow();
        String[] shapes = {"cir", "rex", "flow","cir", "rex"};

        for (String x: shapes) {
            if (x.equals("cir")) {
                cir.draw();
            }else if (x.equals("rex")) {
                rex.draw();
            }else if (x.equals("flow")) {
                flow.draw();
            }
        }
    }

如果使用多态,则不必写那么多if-else分支语句,代码更简单。

    public static void func(Shape shape) {
        shape.draw();
    }

    public static void main(String[] args) {
        Shape[] shapes = {new Cir(), new Rex(), new Flow(),new Rex(), new Rex()};
        for (Shape x: shapes) {
            func(x);
        }
    }

分析: Shape数组:意味着每个元素都是Shope子类,父类类型的数组可以放子类类型变量。
在func方法中,shape引用 引用饿子类对象不一样,调用的方法表现出来的行为也就不一样——这种思想叫做多态。

如果要增加一个打印三角形。
对于类的实现者:定义一个三角形的类,并重写方法。

class Tri extends Shape {
    @Override
    public void draw() {
        System.out.println("画三角形");
    }
}

对于类的调用者:创建一个三角形的实例。

        Tri tri = new Tri();
        func(tri);

改动成本很低
而对于不用多态的情况,就要把if-else进行一定的改动,改动成本更高。

因此多态的优势:
①能够降低代码的“圈复杂度”,避免使用大量的if-else。
②可扩展能力强,如果要新增一种新的形状,使用多态的方式代码改动成本也比较低。
多态的缺陷:
①属性没有多态性,只有方法有多态性:当父类和子类都有同名的属性的时候,通过父类的引用,只能引用父类自己的成员属性。
②构造方法没有多态性,构造方法也不能被重写
③多态代码运行效率降低

  1. 避免在构造方法中调用重写的方法
    有坑的代码,创建两个类,B是父类,D是子类,D中重写func方法。并且在父类B的构造方法中调用func。
class B {

    public B(){
        func();
    }
    public void func() {
        System.out.println("B的方法");
    }
}

class D extends B {
    int num;
    public D() {
        num = 1;
    }
    @Override
    public void func() {
        System.out.println(num + " C重写B的方法");
    }
}


public class TestDemo2 {
    public static void main(String[] args) {
//B是父类,D是子类,D中重写func方法。并且在父类B的构造方法中调用func。
        D d = new D();
    }
}

运行结果:
在这里插入图片描述
分析:在B的构造方法中调func,刻板印象会认为调用的是B的func方法,但是通过实际的运行结果发现调用的是D中的func方法,并且num的值为0,因为此时父类B的构造方法都还没执行完成,更别说子类D的构造了。
注意:当在父类的构造方法当中,去调用父类和子类重写的方法的时候,此时会调用子类的重写方法。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Java多态 的相关文章

  • Keytool 应用程序在哪里?

    我需要在android中使用mapview控件 但我似乎不明白如何运行keytool 是用eclipse安装的吗 我好像找不到下载链接 Thanks keytool http docs oracle com javase 7 docs te
  • HashMap不写入数据库

    我尝试在我的数据库中写入 但只写入发件人和消息 我不明白为什么会发生这种情况 我认为问题出在我使用 sendMessage 的地方 我认为问题是我没有什么可以做的读 写其他用户的主键 我在数据库中写入消息的活动 public class M
  • Android 2.2 SDK - Droid X 相机活动无法正常完成

    我注意到我在 Droid X 上调用的默认相机活动与我的 Droid 和 Nexus One 上的默认相机活动看起来不同 在 Droid 和 Nexus One 上选择 确定 后 活动将完成 Droid X 有一个 完成 按钮 它将带您返回
  • 如果在睡眠线程上调用interrupt()会发生什么?

    我有一个线程 然后run I call sleep 如果我中断这个线程会发生什么 MyThread extends Thread public void run try sleep 1000000 catch InterruptedExce
  • 无法在 Spring Boot 测试中模拟 persistenceContext

    我正在使用带有 Mockito 框架的 spring boot 测试来测试我的应用程序 存储库类 EntityManager 之一作为参考 我的班级如下所示 Repository Transactional Slf4j public cla
  • 如何让spring为JdbcMetadataStore创建相应的schema?

    我想使用此处描述的 jdbc 元数据存储 https docs spring io spring integration docs 5 2 0 BUILD SNAPSHOT reference html jdbc html jdbc met
  • 如何获取 WebElement 的父级[重复]

    这个问题在这里已经有答案了 我试过了 private WebElement getParent final WebElement webElement return webElement findElement By xpath 但我得到
  • 列表应该如何转换为具体的实现?

    假设我正在使用一个我不知道源代码的库 它有一个返回列表的方法 如下所示 public List
  • Git 无法识别重命名和修改的包文件

    我有一个名为的java文件package old myfile java 我已经通过 git 提交了这个文件 然后我将我的包重命名为new所以我的文件在package new myfile java 我现在想将此文件重命名 和内容更改 提交
  • Java 数组的最大维数

    出于好奇 在 Java 中数组可以有多少维 爪哇language不限制维数 但是JavaVM规范将维度数限制为 255 例如 以下代码将无法编译 class Main public static void main String args
  • 如何配置 WebService 返回 ArrayList 而不是 Array?

    我有一个在 jax ws 上实现的 java Web 服务 此 Web 服务返回用户的通用列表 它运行得很好 Stateless name AdminToolSessionEJB RemoteBinding jndiBinding Admi
  • Espresso 和 Proguard 的 Java.lang.NoClassDefFoundError

    我对 Espresso 不太有经验 但我终于成功地运行了它 我有一个应用程序需要通过 Proguard 缩小才能处于 56K 方法之下 该应用程序以 3 秒的动画开始 因此我需要等到该动画结束才能继续 这就是我尝试用该方法做的事情waitF
  • 逃离的正确方法是什么?使用 Oracle 12c MATCH_RECOGNIZE 时 JDBCPreparedStatement 中的字符?

    以下查询在 Oracle 12c 中是正确的 SELECT FROM dual MATCH RECOGNIZE MEASURES a dummy AS dummy PATTERN a DEFINE a AS 1 1 但它不能通过 JDBC
  • JVM:是否可以操作帧堆栈?

    假设我需要执行N同一线程中的任务 这些任务有时可能需要来自外部存储的一些值 我事先不知道哪个任务可能需要这样的值以及何时 获取速度要快得多M价值观是一次性的而不是相同的M值在M查询外部存储 注意我不能指望任务本身进行合作 它们只不过是 ja
  • 挂钩 Eclipse 构建过程吗?

    我希望在 Eclipse 中按下构建按钮时能够运行一个简单的 Java 程序 目前 当我单击 构建 时 它会运行一些 JRebel 日志记录代码 我有一个程序可以解析 JRebel 日志文件并将统计信息存储在数据库中 是否可以编写一个插件或
  • Java:多线程内的 XA 事务传播

    我如何使用事务管理器 例如Bitronix http docs codehaus org display BTM Home JBoss TS http www jboss org jbosstm or Atomikos http www a
  • Hibernate 和可序列化实体

    有谁知道是否有一个框架能够从实体类中剥离 Hibernate 集合以使它们可序列化 我查看了 BeanLib 但它似乎只进行实体的深层复制 而不允许我为实体类中的集合类型指定实现映射 BeanLib 目前不适用于 Hibernate 3 5
  • Android AutoCompleteTextView 带芯片

    我不确定我是否使用了正确的词语来描述此 UI 功能 但我已附上我希望在我的应用程序中实现的目标的快照 它由 Go SMS 使用 用户在编辑文本中键入联系人 在用户从完成下拉列表中选择联系人后 该联系人将被插入到编辑文本中 如附图所示 编辑文
  • 在android中跟踪FTP上传数据?

    我有一个运行 Android 的 FTP 系统 但我希望能够在上传时跟踪字节 这样我就可以在上传过程中更新进度条 安卓可以实现这个功能吗 现在 我正在使用org apache common net ftp我正在使用的代码如下 另外 我在 A
  • 嵌入式 Jetty - 以编程方式添加基于表单的身份验证

    有没有一种方法可以按如下方式以编程方式添加基于表单的身份验证 我用的是我自己的LdapLoginModule 最初我使用基本身份验证并且工作正常 但现在我想在登录页面上进行更多控制 例如显示徽标等 有没有好的样品 我正在使用嵌入式 jett

随机推荐

  • Android获取当前位置的三种方式及其使用方法

    1 GPS定位 2 基站定位 此类位置的获取有赖于手机无线通讯信号 当手机处在信号覆盖范围内 手机可以获得该区域 即通讯术语中的 小区 的识别号 因为这些识别号是惟一的 因此可以将识别号和地理坐标对应起来 因此根据识别号就可以知道地理位置
  • Unity3D 使用TextMeshPro中文字体

    这一篇简单描述一下如果使用unity的一个强大的文字组件或者插件 开始 第一步 如果你是unity2018版本的话 在编辑器里面的AssetPackgeManager找到这个插件 没有的话就在搜索框里面搜索下载 如果你是低于2018的版本
  • Xilinx ISE系列教程(8):读取FPGA芯片唯一ID号

    文章目录 toc 应用场景 方法1 通过JTAG读取 方法2 调用原语读取 DNA PORT原语的使用 DNACLK频率注意 本文是Xilinx ISE系列教程的第8篇文章 用过单片机的朋友都知道 单片机芯片内部都有一串序列号 比如STM3
  • OkHttp库简介

    一直以来 Java并没有什么比较好用的HTTP库 JDK自带的HTTP类又非常旧 难以使用 今天我发现了一个使用比较广泛的OkHttp库 它在安卓和Java领域都有使用 在Github上的星数有两万多 所以我们可以放心的使用 安装 先来看看
  • linkToDeath机制了解和使用

    转自 http www jianshu com p e38f08e34686 在学习Binder和AIDL的过程中遇到的一些有意思的事情 linkToDeath机制 我们先看看官网如何介绍 When working with remote
  • pytorch:参数pin_memory=True和non_blocking=True的作用

    目录 一 pin memory 二 non blocking 一 pin memory pin memory是dataloader 的参数 默认值为False 其作用是是否把把数据存放在锁页内存中 主机的内存根据物理内存 内存条 与虚拟内存
  • 服务器2012系统截屏,Windows Server 2012 R2 Preview界面截图

    微软将在BUILD大会上公开发布Windows 8 1 预览版 Windows 8 1是Windows Blue升级项目的一部分 那么一直以来与Windows客户端如影随形的Windows Server呢 是的 Blue项目并非仅为Wind
  • 微信小程序和APP优劣势大对比

    小程序的优势 1 无需下载 随走随关 2 功能丰富 体验更简便 3 接口众多 可以进行不断的开发 4 流量入口大 背靠日活9 6亿的微信 5 有强大的微信生态环境 小程序对比APP的好处 1 开发成本低 2 开发门槛低 3 获客成本低于Ap
  • 星星之火-47: 5G的八大组网方案

    目录 1 5G组网方案概述 2 选项3系列 4G LTE接入网 5G NR 接入网 4G LTE核心网 3 选项2 5G NR 5G 核心网 4 选项7系列 4G LTE接入网 5G NR接入网 5G核心网 5 选项4系列 4G LTE接入
  • Qt进程间通信

    进程是操作系统的基础之一 一个进程可以认为是一个正在执行的程序 我们可以把进程当做计算机运行时的一个基础单位 关于进程的讨论已经超出了本章的范畴 现在我们假定你是了解这个概念的 在 Qt 中 我们使用 QProcess 来表示一个进程 这个
  • .getClass.getClassLoader.getResourceAsStream的方式加载文件,总是为null加载不到数据

    记录一个问题 我在用如下的代码加载配置文件的时候 总是加载不到数据 文件位置的对的 SparkSessionBase getClass getClassLoader getResourceAsStream spark conf proper
  • Unity之脚本API笔记一(Transform详解及使用方法)

    一 什么是Transform 场景中的每一个物体都有一个Transform 用于存储和操作对象的位置 旋转和缩放 存在层级关系 父级和子级 二 常用变量与属性 位置 1 位置 position 世界坐标 localposition 相对坐标
  • https通讯过程,常见的状态码,DNS解析过程

    一 https通讯过程 1 客户端发起HTTPS请求 然后连接到服务器的443端口 2 传送服务器的证书给客户端 自己颁发的证书需要客户端验证通过 才可以继续访问 而使用受信任的公司申请的证书则不会弹出提示页面 3 客户端收到服务器端的证书
  • JDBC入门

    JDBC 1 JAVA DATABASE CONNECTION 导入jar包 驱动 加载驱动类 Class forName 类名 给出url username password 其中url背下来 jdbc 使用DriverManger来得到
  • Tensorflow学习笔记(一)拟合线性平面 逐句解析

    TensorFlow Python API 依赖 Python 2 7 版本 Python 程序生成了一些三维数据 然后用一个平面拟合它 import tensorflow as tf import numpy as np 使用 NumPy
  • 基于CentOS 7.6安装及配置APISIX 3.0环境

    最近一直在研究微服务相关内容 通过对比各大API网关 发现新起之秀 APISIX无论从开源程度上来讲还是功能上 都拥有很大的优势 经历了几天折磨一样的学习 目前在本地环境中配置成功了一套 以供自己留存吧 实在是网上的很多文章要么太老了 要么
  • 小程序项目实战(二)

    此文章用于总结自己的知识点 有这个项目有兴趣的伙伴可以点击下方链接购买学习 小程序音乐项目开发实战 大神coderwhy新课 学习视频教程 腾讯课堂课程简介https ke qq com course 4162214 一 了解小程序中的基础
  • 自己创建下拉框数组

    自己创建1 8的数组供下拉框选择 this scanPositions new Array 8 fill null map i index gt return label index 1 车 value index 1
  • 2023年第1季社区Task挑战赛开启,等你来战!

    社区Task挑战赛是面向社区开发者开展的代码或教程征集活动 该挑战赛为社区中热爱FISCO BCOS及周边组件的开发者提供了探索区块链技术 挑战技术难题的舞台 该挑战赛去年在社区成功举办了3季 共吸引了数百名开发者报名 前3季都有哪些有趣的
  • Java多态

    关于引用的进一步理解 交换值 因为Java方法在传递参数的时候都是值传递 那么如何通过方法实现2个数的值交换 明确 在传引用的时候 到底拿引用干了个啥 class Value public int a public class Test p