• 【Java基础篇 | 面向对象】--- 聊聊什么是多态(上篇)


    个人主页兜里有颗棉花糖
    欢迎 点赞👍 收藏✨ 留言✉ 加关注💓本文由 兜里有颗棉花糖 原创
    收录于专栏【JavaSE_primary
    本专栏旨在分享学习JavaSE的一点学习心得,欢迎大家在评论区讨论💌
    在这里插入图片描述

    一、什么是多态

    多态的概念:简单来说,多态就是指同一个方法名在不同的对象上有不同的行为。

    多态实现的核心概念是方法的重写和动态绑定。当子类继承父类并重写了父类的方法时,可以通过父类引用指向子类对象,并调用该方法,此时会根据对象的实际类型来决定调用哪个类的方法,从而实现多态性。

    二、多态的实现条件

    在java中,要实现多态的话有3个条件且这三个条件缺一不可:

    1.必须是在继承体系下
    2.子类必须要对父类中的方法进行重写
    3.通过父类的引用调用重写的方法

    那多态具体是如何体现的呢:

    当传递不同类对象时,会调用对应类中的方法。

    三、重写

    重写概念:允许子类重新定义父类中已经存在的方法,并提供自己特定的实现。

    方法重写的规则如下:

    • 子类在重写父类方法时,返回值类型、方法名、参数列表要完全相同。
    • 被重写的方法返回值类型可以不同,但是必须是具有父子类关系的。

    在这里插入图片描述

    • 重写是子类对父类非private修饰、非final修饰、非构造方法等的实现过程进行重新编写。(所以如果父类的方法被private、final、static修饰的话,子类就无法对父类的方法重写。被final修饰的方法称为密封方法,不能被重写)
    • 访问权限不能比父类中被重写的方法的访问权限更低。
    • 构造方法不能被重写(构造方法只能被重载)。
    • 当子类重写父类的方法时,可以使用@Override注解来提醒编译器进行检查,确保方法名和参数正确地覆盖了父类的方法。

    下面是方法重写的代码演示,请看:

    class Person{
        public String name;
        public int age;
    
        Person(String name,int age){
            this.name = name;
            this.age = age;
            System.out.println("Person(String name,int age)");
        }
    
        public void fun(){
            System.out.println(name+",你今天开不开心");
        }
    }
    class Student extends Person {
        public int mark;
    
        public Student(String name,int age,int mark){
            super(name,age);
            this.mark = mark;
            System.out.println("Student(String name,int age,int mark)");
        }
    
        @Override
        public void fun(){
            System.out.println(name+"今天的作业太多了,不是很开心");
        }
    }
    class Teacher extends Person{
        public int wages;
    
        public Teacher(String name,int age,int wages){
            super(name,age);
            this.wages = wages;
            System.out.println("Teacher(String name,int age,int wages");
        }
    }
    public class Test {
        public static void main(String[] args) {
            Student s1 = new Student("Daming",18,100);
            s1.fun();
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43

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

    重载和重写的方法

    重载重写
    方法名称必须相同必须相同
    返回值类型返回值类型不做要求返回值可以相同,但是不同时必须构成父子类关系
    参数列表参数列表不同(个数、数据类型、顺序)参数列表相同
    范围重载的方法要在同一个类中被重写的类和要重写的类必须构成继承关系

    四、向上转型和向下转型

    4.1向上转型

    向上转型:创建一个子类对象,将其当成父类对象来进行使用。
    语法格式:父类类型 对象名 = new 子类类型();

    向上转型的使用场景有三种:直接赋值、方法传参、方法返回。

    这里直接以代码进行举例了:

    class Person{
        public String name;
        public int age;
    
        Person(String name,int age){
            this.name = name;
            this.age = age;
            System.out.println("Person(String name,int age)");
        }
    
        public void fun(){
            System.out.println(name+",你今天开不开心");
        }
    }
    class Student extends Person {
        public int mark;
    
        public Student(String name,int age,int mark){
            super(name,age);
            this.mark = mark;
            System.out.println("Student(String name,int age,int mark)");
        }
    
        @Override
        public void fun(){
            System.out.println(name+"今天的作业太多了,不是很开心");
        }
    }
    class Teacher extends Person{
        public int wages;
    
        public Teacher(String name,int age,int wages){
            super(name,age);
            this.wages = wages;
            System.out.println("Teacher(String name,int age,int wages");
        }
    }
    
    public class Test {
    
        public static void main(String[] args) {
            Student s1 = new Student("Lihua",21,150);
            // p1引用指向了s1
            Person p1 = s1;
    
            // p1.mark;  // error 无法访问,因为Person中没有mark这个属性
    
            // 这里编译的时候编译器认为是访问的Person类中的fun(),但是
            // 程序运行的时候访问的是子类Student中的fun()
            // 此过程成为动态绑定
            s1.fun();
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53

    这里要补充动态绑定和静态绑定两个概念。

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

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

    下面代码就是向上转型的三种使用场景,请看:

    class Person{
        public String name;
        public int age;
    
        Person(String name,int age){
            this.name = name;
            this.age = age;
            System.out.println("Person(String name,int age)");
        }
    
        public void fun(){
            System.out.println(name+",你今天开不开心");
        }
    }
    class Student extends Person {
        public int mark;
    
        public Student(String name,int age,int mark){
            super(name,age);
            this.mark = mark;
            System.out.println("Student(String name,int age,int mark)");
        }
    
        @Override
        public void fun(){
            System.out.println(name+"今天的作业太多了,不是很开心");
        }
    }
    class Teacher extends Person{
        public int wages;
    
        public Teacher(String name,int age,int wages){
            super(name,age);
            this.wages = wages;
            System.out.println("Teacher(String name,int age,int wages)");
        }
    
        public void fun(){
            System.out.println(name+"很开心,因为今天发工资了");
        }
    }
    
    public class Test {
        public static void function(Person p){
            p.fun();
        }
    
        // 向上转型方式三:返回任意子类对象
        public static Person function3(){
            return new Teacher("Simon",21,120);
        }
    
        public static void main(String[] args) {
    
            // 向上转型方式一:直接赋值(子类对象直接赋值给父类对象)
            // Person p1 = new Student("Amy",22,99);
    
            // 向上转型方式二:方法的传参过程中,也可以向上转型
            Student s = new Student("Sam",23,25);
            function(s);
    
            Teacher t = new Teacher("Tom",26,10000);
            function(t);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65

    运行结果如下:
    在这里插入图片描述
    解释在function(Person p)方法中,虽然一个引用调用一个方法,但是因为引用所引用的对象不同,导致调用这个方法所表现的行为不同。这种思想就是多态。

    下面是向上转型的优缺点:

    优点:让代码实现更简单灵活。
    缺点::不能调用到子类特有的方法。

    4.2向下转型

    重要的事情放前面,向下转型是非常不安全的,所以建议平时不要使用向下转型。

    示例一:

    class Person{
        public String name;
        public int age;
    
        Person(String name,int age){
            this.name = name;
            this.age = age;
            System.out.println("Person(String name,int age)");
        }
    
        public void fun(){
            System.out.println(name+",你今天开不开心");
        }
    }
    class Student extends Person {
        public int mark;
    
        public Student(String name,int age,int mark){
            super(name,age);
            this.mark = mark;
            System.out.println("Student(String name,int age,int mark)");
        }
    
        public void getScore(){
            System.out.println("今天出成绩了");
        }
    
        @Override
        public void fun(){
            System.out.println(name+"今天的作业太多了,不是很开心");
        }
    
    
    }
    class Teacher extends Person{
        public int wages;
    
        public Teacher(String name,int age,int wages){
            super(name,age);
            this.wages = wages;
            System.out.println("Teacher(String name,int age,int wages)");
        }
    
        public void getWages(){
            System.out.println("今天发工资哈!");
        }
    
        public void fun(){
            System.out.println(name+"很开心,因为今天发工资了");
        }
    }
    
    public class Test {
        public static void main(String[] args) {
            Person p1 = new Student("Donghua",21,112);
    
            // p1.getScore //error 因为这里只能访问Person类自己特有的方法
    
            Student s1 = (Student)p1;
            s1.getScore(); //这里通过s1可以访问getScore方法
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62

    这里把Person类型强转为子类Student类型。

    示例二:向下转型是指将父类类型的引用转换为子类类型的引用。在向下转型的过程中,需要确保原始对象的实际类型是转换后的子类类型,否则会抛出ClassCastException异常。

    请看代码:

    class Person{
        public String name;
        public int age;
    
        Person(String name,int age){
            this.name = name;
            this.age = age;
            System.out.println("Person(String name,int age)");
        }
    
        public void fun(){
            System.out.println(name+",你今天开不开心");
        }
    }
    class Student extends Person {
        public int mark;
    
        public Student(String name,int age,int mark){
            super(name,age);
            this.mark = mark;
            System.out.println("Student(String name,int age,int mark)");
        }
    
        public void getScore(){
            System.out.println("今天出成绩了");
        }
    
        @Override
        public void fun(){
            System.out.println(name+"今天的作业太多了,不是很开心");
        }
    
    
    }
    class Teacher extends Person{
        public int wages;
    
        public Teacher(String name,int age,int wages){
            super(name,age);
            this.wages = wages;
            System.out.println("Teacher(String name,int age,int wages)");
        }
    
        public void getWages(){
            System.out.println("今天发工资哈!");
        }
    
        public void fun(){
            System.out.println(name+"很开心,因为今天发工资了");
        }
    }
    
    public class Test {
        public static void main(String[] args) {
            Person p1 = new Student("Jame",21,115);
            Teacher t1 = (Teacher)p1;
            t1.getWages();
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59

    示例二运行结果如下:
    在这里插入图片描述

    示例二解释首先创建了一个Student对象,并将其赋值给了一个Person类型的变量p1。这是一种向上转型的操作,可以安全地进行,因为Student是Person的子类。

    接着尝试将p1向下转型为Teacher类型,即将其赋值给一个Teacher类型的变量t1。这是一种不安全的操作,因为p1实际上是Student类型的对象。运行这段代码会抛出ClassCastException异常。

    示例二解决方式,请看代码:

    Person p1 = new Student("Jame", 21, 115);
    if (p1 instanceof Teacher) {
        Teacher t1 = (Teacher) p1;
        t1.getWages();
    } 
    else {
        System.out.println("p1不是Teacher类型的对象");
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    好了,以上就是本文的全部内容啦,就到这里吧!!!
    再见啦友友们!!!

  • 相关阅读:
    【爬虫】Python使用动态IP,多线程,爬取uncomtrade的数据
    【国外框架】—— quasar 应用程序插件
    Yocto Beaglebone-Black 编译记录
    第4关: 网页排序——PageRank算法
    Text-to-SQL小白入门(六)Awesome-Text2SQL项目介绍
    【校招VIP】前端项目开发之正则表达
    Elasticsearch:通过 JDBC 使用 SQL 来查询索引 - DBeaver
    【数据结构】二叉树的顺序结构-堆
    2022上行部落的学习和实践总结
    文章主要内容提取软件[基于NLP技术]
  • 原文地址:https://blog.csdn.net/m0_74352571/article/details/132850983