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

Java的多态

Java的多态

​ 同种类型的对象,表现出的不同的形态

一、多态的表现形式

  • 父类类型 对象名称 = 子类对象;

二、多态的前提

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

好处:使用父类型作为参数,可以接收所有的子类对象,体现多态的扩展性与便利

三、多态调用成员的特点

  • 变量调用编译看左边,运行也看左边
  • 方法调用编译看左边,运行看右边

多态调用成员的内存图解:

在这里插入图片描述

  1. 测试类Test的字节码文件先加载到方法区中,虚拟机会自动的调用main方法加载到栈
  2. 第一行代码,用到了两个类,那必定是先加载父类的字节码文件此处应该还有Object类的字节码文件,但是此处用不到,就省略了,所以先加载Animal类的字节码文件,并挂上虚方法表;接下来加载Dog类的字节码文件,与此同时会把父类的虚方法表继承下来,并把show方法进行一个覆盖,并建立联系(Dog类会记录自己的父类Animal类字节码文件的位置)
  3. 第一行代码,左边Animal a会在栈区开辟一个小空间,右边遇见new关键字,会在堆区开辟一个空间,一部分存储从父类继承下来的成员变量的信息,另一部分则存储自己的成员变量的信息,赋值并将地址值传给栈区的变量a的空间中
  4. 第二行代码,用多态方法调用成员变量,(编译看左边)虚拟机会先看栈区空间的父类区域,若有该成员变量,则编译成功,否则编译失败,(运行也看左边)且运行也会直接看父类空间中的name的值,并获取打印出来【若没有使用多态创建对象,调用对象会首先在子类区域寻找,没有才会从父类中寻找
  5. 第三行代码,用多态方式调用成员方法,(编译看左边)在编译的时候会先去找父类里面有没有show方法,如果没在父类中没找到则会报错,(运行看右边)在实际运行时看的是子类的show方法(虚方法表中),所以子类的show方法会被加载进栈,并运行
示例代码
public class Test {
    public static void main(String[] args) {
        // 创建对象(多态方式)
        Animal a = new Dog();

        // 调用成员变量时:编译看左边,运行也看左边
        // 编译看左边:javac编译代码的时候,会看左边的父类中有没有这个变量,如果有,编译成功,如果没有编译失败
        // 运行也看左边:java运行代码的时候,实际获取的就是左边父类中成员变量的值
        System.out.println(a.name); // 动物

        // 调用成员方法:编译看左边,运行看右边
        // 编译看左边:javac编译代码的时候,会看左边的父类中有没有这个方法,如果有,编译成功,如果没有则编译失败
        // 运行看右边;java运行代码的时候,实际上运行的是子类中的方法
        a.show();   // Dog——show方法

        // 成员变量:a是Animal类型的,所以默认都会从Animal这个类中去找
        // 成员方法:如果子类对方法进行了重写,那么在虚方法表中是会把父类中的方法进行覆盖的
    }
}

class Animal {
    String name = "动物";

    public void show() {
        System.out.println("Animal——show方法");
    }
}

class Cat extends Animal{
    String name = "小猫";
    @Override
    public void show() {
        System.out.println("Cat——show方法");
    }
}

class Dog extends Animal{
    String name = "小狗";

    @Override
    public void show() {

        System.out.println("Dog——show方法");
    }
}

四、多态的优势和弊端

优势:

  • 方法中,使用父类型作为参数,可以接收所有子类对象

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

Person p = new Student ();
p.work(); // 业务逻辑发生改变时,后序代码无需修改
  • 定义方法时,使用父类型作为参数,可以接收所有子类对象,体现多态的扩展性与便利

弊端:

  • 不能使用子类的特有功能

若要使用子类的特有功能则必须进行类型转换

引用数据类型的类型转换:
  • 自动类型转换:(低转高)
Animal a = new Dog();
  • 强制类型转换:(高转低)
Dog b = (Dog)a;

进行强制类型转化时,可以使用instanceof关键字来判断和转换(如下示例代码中)

强制类型转换能解决的问题
  • 可以转换成真正的子类类型,从而调用子类独有的功能
  • 转换类型与真实对象类型不一致会报错
  • 转换的时候用instanceof关键字进行判断

示例代码:

public class Test {
    public static void main(String[] args) {
        // 自动类型转化
        Animal a = new Dog();
        a.eat();	// 输出: 狗吃骨头

        // 多态的弊端
        // a.lookhome()不能调用子类的特有功能
        // 因为在多态中方法的调用是编译看左边————而左边父类中没有子类的特有方法,导致编译失败
        // 若要实现只能通过强制类型转化(但转化的类型必须对应好)

        Dog b = (Dog)a;
        b.lookhome();	// 输出: 狗在看家
        // 可以通过instanceof判断类型在进行强转
        if (a instanceof Dog) {
            Dog c = (Dog)a;
            c.lookhome();	// 输出: 狗在看家
        }else if (a instanceof Cat) {
            Cat c = (Cat)a;
            c.catchmouse();	// 输出: 狗在看家
        }else {
            System.out.println("没有此类型,无法转换");
        }
        // JDK14之后instanceof出了更简洁的方法
        if (a instanceof Dog d) {
            d.lookhome();
        }else if (a instanceof Cat d) {
            d.catchmouse();
        }else {
            System.out.println("没有这个类型,无法转换");
        }
    }
}

class Animal {
    public void eat() {
        System.out.println("动物在吃东西");
    }
}

class Dog extends Animal {
    @Override
    public void eat() {
        System.out.println("狗吃骨头");
    }

    public void lookhome() {
        System.out.println("狗在看家");
    }
}

class Cat extends Animal {
    @Override
    public void eat() {
        System.out.println("猫在吃小鱼干");
    }

    public void catchmouse() {
        System.out.println("猫正在抓老鼠");
    }
}

五、综合练习:

在这里插入图片描述

Animal类:

public class Animal {
    private int age;
    private String color;

    public Animal() {

    }

    public Animal(int age, String color) {
        this.age = age;
        this.color = color;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public int getAge() {
        return age;
    }

    public void setColor(String color) {
        this.color = color;
    }

    public String getColor() {
        return color;
    }

    public void eat(String something) {
        System.out.println("动物在吃" + something);
    }

}

Person类:

public class Person {
    private String name;
    private int age;

    public Person() {

    }

    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public void keepPet(Animal an, String something) {
        if(an instanceof Dog d) {
            System.out.println("年龄为" + age + "岁的" + name + "养了一只" + an.getColor() + "的" + an.getAge() + "岁的狗");
            d.eat("骨头");
            d.lookhome();
        } else if (an instanceof Cat c) {
            System.out.println("年龄为" + age + "岁的" + name + "养了一只" + an.getColor() + "的" + an.getAge() + "岁的猫");
            c.eat("鱼");
            c.catchMouse();
        } else {
            System.out.println("没有这种动物");
        }
    }
}

Dog类:

public class Dog extends Animal{
    public Dog() {

    }
    public Dog(int age, String color) {
        super(age, color);
    }
    public void lookhome() {
        System.out.println("狗在看家");
    }

    public void eat(String something) {
        System.out.println(getAge() + "岁的" + getColor() + "的狗两只前腿死死的抱住" + something + "猛吃");
    }
}

Cat类:

public class Cat extends Animal{
    public Cat() {

    }
    public Cat(int age, String color) {
        super(age, color);
    }
    public void catchMouse() {
        System.out.println("猫在逮老鼠");
    }
    public void eat(String something) {
        System.out.println(getAge() + "岁的" + getColor() + "的猫眯着眼睛侧着头吃" + something);
    }
}

测试类:

public class Test {
    public static void main(String[] args) {
        Person p1 = new Person("老王", 30);
        Dog d = new Dog(2, "黑颜色");
        p1.keepPet(d, "骨头");

        Person p2 = new Person("老李", 25);
        Cat c = new Cat(3, "灰颜色");
        p2.keepPet(c, "鱼");

    }
}

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


http://www.kler.cn/a/374910.html

相关文章:

  • Webpack入门教程:从基本概念到优化技巧
  • 再探“构造函数”
  • Ethernet 系列(6)-- 基础学习::OSI Model
  • 责任链模式:解耦请求处理的设计模式
  • 我为何要用wordpress搭建一个自己的独立博客
  • 2023-2024年教育教学改革、教学成果奖等项目申请书合集-最新出炉 附下载链接
  • LEADTOOLS 版本 23 现已发布,引入了 Excel API等众多新功能!
  • 就业市场变革:AI时代,我们将如何评估人才?
  • Python之groupby()及aggregate()方法
  • 手机实时提取SIM卡打电话的信令声音-新的篇章(三、Android虚拟声卡探索)
  • 每日互动基于 Apache DolphinScheduler 从容应对ClickHouse 大数据入库瓶颈
  • 巨好看的登录注册界面源码
  • 【 纷享销客-注册安全分析报告-无验证方式导致安全隐患】
  • C++:二叉搜索树进阶
  • flink 自定义kudu connector中使用Metrics计数平均吞吐量,并推送到自定义kafkaReporter
  • DDIM扩散模型的加速采样(去噪)算法 Denoising Diffusion Implicit Models
  • windows 11 配置 kafka 使用SASL SCRAM-SHA-256 认证
  • 操作符详解
  • Java第二阶段---15异常---第三节 自定义异常
  • 【智能算法应用】秃鹰搜索算法求解二维路径规划问题
  • 适合视频搬运的素材网站推荐——短视频素材下载宝库
  • DirectShow过滤器开发-写MP3音频文件过滤器(再写 写MP3)
  • 鸿蒙系统的优势 不足以及兼容性与未来发展前景分析
  • C++基础_类的基本理解
  • 『 Linux 』网络传输层 - TCP(二)
  • NLP算法工程师精进之路:顶会论文研读精华