首页 > 其他分享 >多态

多态

时间:2024-09-08 22:51:07浏览次数:9  
标签:name show 多态 Dog Animal 父类 public

多态:polymorphism

概述

有了封装才有面向对象,有了面向对象才有继承和多态。

什么是多态:同类型的对象,表现出的不同形态。

多态的表现形式:父类类型 对象名称 = 子类对象;

多态的前提:

  1. 有继承或实现关系(实现与接口有关)

  2. 有父类引用指向子类对象,Fu f = new Zi();

  3. 要有方法重写。

程序示例:

父类:

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 show() {
        System.out.println(name + ", " + age);
    }
}

子类:

public class Administrator extends Person {
    @Override
    public void show() {
        System.out.println("管理员的信息为:" + getName() + ", " + getAge());
    }
}

子类:

public class Student extends Person {
    @Override
    public void show() {
        System.out.println("学生的信息为:" + getName() + ", " + getAge());
    }
}

子类:

public class Teacher extends Person {
    @Override
    public void show() {
        System.out.println("老师的信息为:" + getName() + ", " + getAge());
    }
}

测试类:

public class Test {
    public static void main(String[] args) {
        // 创建三个对象,调用 register 方法
        Student s = new Student();
        s.setName("zhangsan");
        s.setAge(23);

        Teacher t = new Teacher();
        t.setName("lisi");
        t.setAge(24);

        Administrator ad = new Administrator();
        ad.setName("wangwu");
        ad.setAge(25);

        // 调用 register 方法
        register(s);
        register(t);
        register(ad);
    }

    // 定义一个方法,表示注册,要求这个方法可以接收管理员对象、教师对象和学生对象
    // 这就要求形参必须是三个类型的父类
    public static void register(Person p) {  // 当看见一个方法的形参是一个类,那么可以给这个方法传递的实参除了这个类的对象,还包括这个类的所有子类的对象
        p.show();  // 可以根据传递进来的不同对象,执行不同的 show() 方法
    }
}

执行结果:

学生的信息为:zhangsan, 23
老师的信息为:lisi, 24
管理员的信息为:wangwu, 2

多态调用成员的规则:

调用成员变量:编译看左边,运行看左边。

调用成员方法:编译看左边,运行看右边。

程序示例:

public class Test {
    public static void main(String[] args) {
        // 用多态的方式创建对象
        Animal a = new Dog();  // 这是自动类型转换

        // 调用成员变量
        // javac 编译时看左边的父类中有没有这个变量,如果有,编译成功,如果没有,编译失败
        // java 运行时获取左边父类中成员变量的值
        System.out.println(a.name);  // Animal

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

        // a 是 Animal 类型的,所以用 a 调用成员变量或成员方法时,默认从 Animal 这个类中去找。
        // 用 a 调用成员变量时,会把父类中的成员变量也继承下来,父类里面有一个 name,子类里面也有一个 name。现在 a 是父类类型,所以调用父类的 name
        // 用 a 调用成员方法时,如果子类中对方法进行了重写,那么在子类中会对父类方法进行覆盖,所以实际调用的是子类中重写的方法。
    }
}

class Animal {
    String name = "Animal";

    public void show() {
        System.out.println("调用 Animal 类的 show() 方法。");
    }
}

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

    @Override
    public void show() {
        System.out.println("调用 Dog 类的 show() 方法。");
    }
}

class Cat extends Animal {
    String name = "cat";

    @Override
    public void show() {
        System.out.println("调用 Cat 类的 show() 方法。");
    }
}

内存分析:

第一步,测试类的字节码文件进入方法区。

第二步,虚拟机自动调用 main() 方法,所以 main() 方法进栈。下面开始执行 main() 方法里面的第一行代码。

第三步,执行语句 Animal a = new Dog();,将类的字节码文件加载到内存中的时候,永远是先加载父类,再加载子类。此处 Dog 类的父类是 Animal,Animal 类的父类是 Object。因此此处先加载 Object,然后加载 Animal,然后加载 Dog。在方法区中,Dog 这个字节码文件和其父类 Animal 的字节码文件之间还有一个联系,即 Dog 会记住 Animal 的字节码文件在方法区中的位置。等号的左边在栈中开辟了一个小空间,即变量 a,类型为 Animal,等号的右边有关键字 new,因此在堆空间中开辟了一个小空间,假设这个空间的地址值为 001,这个空间会被分为两部分,一部分存储从父类中继承下来的成员变量信息,另一部分用来存储自己的成员变量信息。初始化完成后会把地址值 001 赋给变量 a,a 就可以通过 001 找到堆空间中的这个对象。到这里这一条语句才算执行完毕。

第四步,执行语句 System.out.println(a.name); 时用 a 调用变量 name。用多态的方式定义的变量,在编译和运行时都是看左边,即看父类。此处 a 通过 001 去看堆空间中的那部分内存中,用来存储从父类继承下来的成员变量的那部分空间,看这部分空间中有没有要调用的这个成员变量,此处为 name,如果有 name,那么编译成功,如果没有则编译失败。运行时还是去这部分空间找到这个成员变量,获取它的值。如果上一条语句为 Dog d = new Dog();,则用 d 访问成员变量 name 时,即执行 d.name 时,不会去存放父类成员变量的那部分空间去找 name,而是去存放子类自己的成员变量的那部分空间去找 name。如果没找到,才会去存放父类的成员变量的那部分空间去找 name。

第五步,执行语句 a.show();,用多态的方式定义的对象,当这个对象调用方法时,编译看左边,即看父类,执行看右边,即看子类。编译的时候,会去方法区的父类的字节码文件中查看有没有 show() 方法,如果找到了,不会报错,代码正常。如果没有找到,代码报错。实际运行时,会在方法区的子类的字节码文件中查找 show() 方法。而子类可以重写这个 show() 方法去覆盖从父类中继承来的 show() 方法,因此不同的对象执行的是各个不同子类的自己的方法。

多态的优势

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

Person p = new Student;
p.work();        // 业务逻辑发生改变时,后续代码无需修改

定义方法的时候,使用父类型作为参数,可以接收所有子多类对象,体现多态的扩展性与便利。

多态的弊端

不能调用子类特有的方法,即不存在于父类但是存在于子类的方法。因为用多态的方式定义的对象,在调用一个方法时,编译时看父类,运行时看子类,而父类中没有这个方法,则编译时报错。

解决方案:

利用类型的强制转换,将对象的类型由父类的类型转换为子类的类型。父类的范围比子类的范围大,就好像 int 的范围比 byte 的范围大。

程序示例:

Animal a = new Dog();
Dog d = (Dog) a;    // 这个 d 对象就可以执行 Dog 类有而 Animal 类没有的方法

关键字 instanceof 用于检查一个对象是否是一个类的实例。用法:对象名 instanceof 类名,返回 boolean 值。

程序示例:

Animal a = new Dog();
if (a instanceof Dog) {
    Dog d = (Dog)a;
} else if (a instanceof Cat) {
    Cat c = (Cat) a;
} else {
    System.out.println("没有这个类型,无法转换");
}

程序示例:

public class Test {
    public static void main(String[] args) {
        // 用多态的方式创建对象
        Animal a = new Dog();

        // Cat b = (Cat) a; // ClassCastException: class demo1.Dog cannot be cast to class demo1.Cat
        if (a instanceof Dog) {
            Dog d = (Dog) a;
        } else if (a instanceof Cat) {
            Cat c = (Cat) a;
        } else {
            System.out.println("没有这个类型,无法转换");
        }
    }
}

class Animal {
    String name = "Animal";

    public void show() {
        System.out.println("调用 Animal 类的 show() 方法。");
    }
}

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

    @Override
    public void show() {
        System.out.println("调用 Dog 类的 show() 方法。");
    }
}

class Cat extends Animal {
    String name = "cat";

    @Override
    public void show() {
        System.out.println("调用 Cat 类的 show() 方法。");
    }
}

Java 14 开始添加了一个新特性,可以将判断和强制转换合在一起写,写成一行。

Animal a = new Dog();
// 先判断 a 是否为 Dog 类型,如果是,则将 a 强制类型转化为 Dog 类型并赋值给新建的一个变量 d,如果不是则不强转
if (a instanceof Dog d) {
    d.lookHome();    // d 对象调用 Dog 类独有的方法 lookHome()
} else if (a instanceof Cat c) {

} else {
    System.out.println("没有这个类型,无法转换");
}

标签:name,show,多态,Dog,Animal,父类,public
From: https://www.cnblogs.com/Chengkai730/p/18403656

相关文章

  • 简单谈谈方法重载和方法重写(编译时多态和运行时多态)
    这篇文章来聊一聊方法重载和重写到底是有什么区别,重载实现的是编译时多态,而方法重写实现的是运行时多态,那什么又是编译时多态和运行时多态呢?定义一个Animal类,publicclassAnimal{publicvoidsay(intage){System.out.println("我的年龄是"+age);}......
  • 【Java 基础】:三大特征之多态
    ✨                         杏花疏影里,吹笛到天明    ......
  • java多态的编译执行
    多态执行“编译看左边,运行看右边”“成员变量,静态方法看左边;非静态方法:编译看左边,运行看右边。”意思是:当父类变量引用子类对象时(Fuf=newZi();),在这个引用变量f指向的对象中,他的成员变量和静态方法与父类是一致的,他的非静态方法,在编译时是与父类一致的,运行时却与子类......
  • Python中的方法重写与多态:解锁编程的无限可能
    在编程的世界里,灵活性与扩展性往往是衡量一个语言是否强大、易于维护的关键指标。Python,作为一种被广泛使用的高级编程语言,不仅以其简洁易读的语法赢得了众多开发者的喜爱,更因其支持多种面向对象特性而备受青睐。其中,“方法重写”与“多态”便是两个核心概念,它们不仅能够极......
  • Python中的方法重写与多态:解锁编程的无限可能
    在编程的世界里,灵活性与扩展性往往是衡量一个语言是否强大、易于维护的关键指标。Python,作为一种被广泛使用的高级编程语言,不仅以其简洁易读的语法赢得了众多开发者的喜爱,更因其支持多种面向对象特性而备受青睐。其中,“方法重写”与“多态”便是两个核心概念,它们不仅能够极大地提高......
  • 11-6类的多态和多态性
    多态指的是一类事物有多种形态,比如动物有多种形态:猫、狗、猪classAnimal:#同一类事物:动物deftalk(self):passclassCat(Animal):#动物的形态之一:猫deftalk(self):print('喵喵喵')classDog(Animal):#动物的形态之二:狗deftalk(self)......
  • 《C++中的面向对象编程三大特性:封装、继承与多态》
    在C++编程的广阔世界中,面向对象编程(Object-OrientedProgramming,OOP)的三大特性——封装、继承和多态,犹如三把强大的利器,帮助程序员构建出高效、可维护和可扩展的软件系统。本文将深入探讨如何在C++中实现这三大特性,并通过具体的代码示例展示它们的强大之处。一、封装(Enca......
  • Java中多态的学习
    多态目录多态多态的概念为什么要使用多态多态存在的三个必要条件多态的实现方式多态的分类方式一方式二多态的机制原理多态的概念多态是同一个行为具有多个不同表现形式或形态的能力。多态就是同一个接口,使用不同的实例而执行不同操作。为什么要使用多态消除类型之间的耦......
  • PHP8面向对象快速入门四 类的多态 方法重载
    在面向对象编程中,多态是指相同的操作或方法可以作用于不同的对象,从而产生不同的结果。方法重写方法重写是子类提供对从父类继承的方法的特定实现。这是实现多态的一种方式。方法重写允许子类提供具体的实现,而不是使用父类中定义的实现。示例: <?phpclassAnimal{pub......
  • Python多态
    #1多态#指同一种行为具有不同的表现形式#1.1多态的前提#继承#重写#classAnimal:#  defspeak(self):#    print('动物')#classDog(Animal):#  defspeak(self):#    print('狗')##classCat(Animal):#  defspeak(se......