Java:进一步理解多态性

news2024/11/22 21:50:57

Java:进一步理解多态性

在这里插入图片描述


每博一文案

有人说我心里有事,但我谁也不想说,沉默不是没有情绪,而是我明白了,说了又没有意义,
比起诉说的委屈和不甘,沉默或许更好。当我们经历越多真实与虚假,看清了世界的真相后,
不仅没有那么多的酸性。反而会变得越来越沉默,越来越不想说,因为这世上人心凉薄,能让
我们交心的人寥寥无几。
有人说,生活的本质就是一个人活着,不要对别人心存太多期待,倘若没有过多的欢喜,并不会有极度的悲伤,
人只有寒心一次。才知道世界的真真假假,被最信任的人欺骗一次,才明白这世上没有一成不变的心,
有些话不再逢人就说,有些路,也不再期待有谁能同行哪些苦衷,背叛,不是没感觉而是知道说与不说都一样。
那些伤口不是不在乎,而是明白,除了咬牙硬撑,别无他法。
我们那些惊天动地的伤痛,在别人眼里不过是随手扶过的尘埃,慢慢的开始,不悲不痛,也不再期望什么。
逢人不必延申,孤独本事常态,倘若深情别辜负,余生孤独又何妨,懂我们的人不用解释,不懂我们的人,
百口莫辩。成年人的世界万般皆苦,只可自渡,一个人成熟的标志是学会了沉默,因为看淡了许多事,
看清了许多人。
愿你懂得沉默,做好眼前的事,内心充盈,从此不喧哗,不声张,有不动声色的脸。
                                           ——————   一禅心灵庙语

文章目录

  • Java:进一步理解多态性
    • 每博一文案
    • 1. 面向对象特征之一: 多态性
      • 1.2 多态的重要性
    • 2. 多态是:运行时类型 “编译看左边,运行看右边”
    • 3. 重写
      • 3.1 方法重写, 变量不重写
    • 4. 多态的总结
    • 5. 最后:


1. 面向对象特征之一: 多态性

多态性 是面向对象中最重要的概念,在Java中的体现:

  1. 对象的多态性: 父类的引用指向子类的对象 。其实多态性,可以理解为是 向上转型 ,只是大多是程序员,不说向上转型,而是说多态。

  2. 可以应用在 抽象类接口 上。

  3. Java引用变量有两个类型:编译时类型运行时类型

    • 编译时类型: 由声明该变量时使用的类型决定。
    • 运行时类型: 由实际赋给该变量的对象决定,简称:编译时,看左边;运行时,看右边。
  4. 若编译时类型和运行时类型不一致,就出现了对象的多态性

  5. 多态情况下:看左边: 看父类的引用(父类中不具备子类特有的方法和变量);看右边: 看的是子类的对象 (实际运行的是子类重写父类的方法)。

1.2 多态的重要性

  1. 实现了代码的通用性
  2. JDBC : 使用Java程序操作(获取数据库连接,CRUD)数据库(MYSQL,Oracle,DB2,SQL Server)
  3. 抽象类,接口的使用体现了多态性:(抽象类,接口是不能 new 实例化的),所以我们只能调用其中的实现的抽象类,接口的 了,而如果没有 **多态性(向上转型)**这个特点,我们就无法调用 对应 抽象类,接口 实现的子类了。这样导致 抽象类,接口无法使用了。

2. 多态是:运行时类型 “编译看左边,运行看右边”

多态是运行时类型: 简单的说就是我们从编译上无法看出,所要指执行的结果,而是 运行 以后才能知道结果。

所以对于多态的执行的运行结果: “编译看左边,运行看右边”

如下代码:

package blogs.blog1;

public class PersonTest{
    public static void main(String[] args) {
        Person p1 = new Student();   // 多态: 父类的引用执行子类对象
        p1.show();    // 执行的是 Student()子类重写的方法,
    }

}

class Person {   // 父类
    String name;
    int age;

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

    }

    public void show() {
        System.out.println("我是 Person");
    }

}

class Student extends Person{   // 子类
    int studentId;

    @Override
    public void show() {
        System.out.println("我是 Student");
    }

}


class Teacher extends  Person{  // 子类
    int teacherId;

    @Override
    public void show() {
        System.out.println("我是 Teacher");
    }
}

在这里插入图片描述


提出其中一部分,重要的代码分析:

public class PersonTest{
    public static void main(String[] args) {
        Person p1 = new Student();   // 多态: 父类的引用执行子类对象
        p1.show();    // 执行的是 Student()子类重写的方法,
    }

}

上述代码 Person p1 = new Student() 明明 编译时 赋值的对象是 Person 父类类型,可我们调用运行时 执行的却是其中子类 Student的 show() 方法。这就体现了,多态是运行时类型 。我们通过编译无法确定执行的结果。而是该对应的方法运行时 执行后,才可以确定结果(调用的是子类的方法)。简单的理解就是 :编译看左边,运行看右边

这句话对应上述代码就是:编译的时候,看左边:new Student()赋值给的是 Person 类型的,而实际运行的结果,看右边:运行的结果执行的是 Student 子类中的 show 方法。

我们再看如下代码:

public class PersonTest{
    public static void main(String[] args) {
        Person p1 = new Student();   // 多态: 父类的引用执行子类对象
        p1.show();    // 执行的是 Student()子类重写的方法,

        Person p2 = new Teacher();  // 多态:编译看左边,运行看右边
        p2.show();    // 实际执行的是 Teacher() 子类重写的方法
    }

}

在这里插入图片描述



正常情况的调用:

我们编译对象是什么类型的,我们就执行所对应对象下的方法,变量。如:编译的是 Person 类型的,执行的就是 Person的方法/变量。

public class PersonTest{
    public static void main(String[] args) {
        Person person = new Person();
        person.show();

        Student student = new Student();
        student.show();

        Teacher teacher = new Teacher();
        teacher.show();
        
    }
}

在这里插入图片描述


虚拟方法调用(多态的情况下)

子类中定义了 与 父类同名同参数 的方法,在多态情况下 ,将此时父类的方法称为 虚拟方法 ,父类根据赋给它的不同子类对象,动态调用属于子类的该方法。这样的方式导致方法调用在 编译器是无法确定的。

只有等到方法调用的那一刻,解释运行器才会确定所要调用的具体方法,这称为“晚绑定”或“动态绑定”。

public class PersonTest{
    public static void main(String[] args) {
        Person p1 = new Student();   // 多态: 父类的引用执行子类对象
        p1.show();    // 执行的是 Student()子类重写的方法,

        Person p2 = new Teacher();  // 多态:编译看左边,运行看右边
        p2.show();    // 实际执行的是 Teacher() 子类重写的方法
    }

}

需要注意的点: 多态中,子类和父类都有的同名变量,是不会被重写的(调用的就不是子类中的变量了)。调用的仅仅只是编译赋值类型中的变量,在多态中:子类和父类同名的变量。编译时和运行时都是看左边.

如下代码:

package blogs.blog1;


public class PersonTest{
    public static void main(String[] args) {
        Person p1 = new Student();
        System.out.println(p1.name);  // 调用的是 父类 Person中的 name
        
        Person p2 = new Teacher();
        System.out.println(p2.name);  // 同样调用的还是,父类 Person 中的 name 方法
    }

}

class Person {   // 父类
    String name = "Person";
    int age;

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

    }

    public void show() {
        System.out.println("我是 Person");
    }

}

class Student extends Person{   // 子类
    int studentId;
    String name = "Student";

    @Override
    public void show() {
        System.out.println("我是 Student");
    }

}


class Teacher extends  Person{  // 子类
    int teacherId;
    String name = "Teacher";

    @Override
    public void show() {
        System.out.println("我是 Teacher");
    }
}
public class PersonTest{
    public static void main(String[] args) {
        Person p1 = new Student();
        System.out.println(p1.name);  // 调用的是 父类 Person中的 name

        Person p2 = new Teacher();
        System.out.println(p2.name);  // 同样调用的还是,父类 Person 中的 name 方法
    }
}

在这里插入图片描述


注意: 在多态(向上转型),一个引用类型变量,如果声明为父类的类型,但实际引用的是子类对象,那么该变量是不能 再访问 子类中所特有的属性和方法 的。编译上是无法通过的。因为你左边赋值的是一个父类的类型,却想调用 父类中没有的属性和方法(就是子类中特有的属性和方法) 编译是无法通过的。虽然你无法调用访问子类中特有的属性和方法,但是对于 new 了子类对象,其中的属性和方法是会加载到内存当中的,无法访问不代表没有加载到内存当中,继承中父类中的 private 属性和方法,也是同样的,无法调用,不代表没有加载到内存当中。这是两回事。

如下代码

package blogs.blog1;


public class PersonTest{
    public static void main(String[] args) {
        Person p1 = new Student();  // 多态:父类的引用指向子类的对象
        System.out.println(p1.studentId);// 调用子类特有的属性,编译无法通过
        p1.ncee(); // 调用子类特有的方法,编译无法通过
        // 因为我们左边编译赋值的是 Person 父类类型,该父类中没有子类特有的属性和方法,编译自然无法通过了。
        // 编译都无法通过,更无法调用运行了。

    }
}

class Person {   // 父类
    String name = "Person";
    int age;

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

    }

    public void show() {
        System.out.println("我是 Person");
    }



}

class Student extends Person{   // 子类
    int studentId;   // 该子类特有的属性

    @Override
    public void show() {
        System.out.println("我是 Student");
    }

    // 该子类特有的方法
    public void ncee() {
        System.out.println("高考");
    }

}


class Teacher extends  Person{  // 子类
    int teacherId;     // 该子类特有的属性

    @Override
    public void show() {
        System.out.println("我是 Teacher");
    }


    // 该子类特有的方法
    public void teach() {
        System.out.println("教书育人");
    }
}

在这里插入图片描述


3. 重写

基于 继承,多态 在子类中可以根据需要对从 父类 中继承而来的方法进行改造。

在子类中可以根据需要对从父类中继承来的方法进行改造,也称为 方法的重置,覆盖,重写 。在程序执行时,子类的方法将覆盖父类的方法。

重写的要求:

  1. 子类重写的方法必须和父类被重写的方法 具有相同的方法名以及参数列表
  2. 子类重写的方法返回值类型必须 <= 父类被重写的方法的返回值类型
  3. 子类重写的方法使用的访问权限必须 >=父类被重写的方法的访问权限,特殊的 public 权限是最大的,父类声明了 public 子类就必须声明 public 。
  4. 子类不能重写 父类中声明为 private 权限的方法
  5. 子类方法抛出的 异常必须 <= 父类被重写方法的异常

注意点:

  1. 子类与父类中同名同参数的方法必须同时声明为非 static 的才可以重写,获取同时声明为 static 的(不是重写)。因为 static 方法时属于类的,子类无法覆盖父类的方法。
  2. 重写的是方法,对于变量是没有重写,这种说法的.

如下代码:

package blogs.blog1;


public class PersonTest{
    public static void main(String[] args) {
        Person p1 = new Student();  // 多态
        p1.show();  // 调用的是 Student子类中重写的方法
        
        Person p2 = new Teacher();  // 多态
        p2.show();  // 调用的是 Teacher()子类重写的方法
    }

}

class Person {   // 父类
    String name = "Person";
    int age;

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

    }

    public void show() {
        System.out.println("我是 Person");
    }



}

class Student extends Person{   // 子类
    int studentId;

    @Override
    public void show() {
        System.out.println("我是 Student");
    }

    public void ncee() {
        System.out.println("高考");
    }

}


class Teacher extends  Person{  // 子类
    int teacherId;

    @Override
    public void show() {
        System.out.println("我是 Teacher");
    }


    public void teach() {
        System.out.println("教书育人");
    }
}

在这里插入图片描述

3.1 方法重写, 变量不重写

注意: 重写的对象是方法,变量是不会重写的,当父类/子类中都含有同名的变量,看左边编译赋值的是什么类型(子类/父类) 就调用那个类中定义的变量

如下代码:

package blogs.blog1;


public class PersonTest{

    public static void main(String[] args) {
        Person p1 = new Student();
        System.out.println(p1.name);  // 对于编译看左边,调用的是 Person中定义的 name的变量值

        Person p2 = new Teacher();
        System.out.println(p2.name); // 同理的,调用的是左边编译 Person 中定义的 name的变量值

    }
}

class Person {   // 父类
    String name = "Person";
    int age;

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

    }

    public void show() {
        System.out.println("我是 Person");
    }



}

class Student extends Person{   // 子类
    int studentId;
    String name = "Student";

    @Override
    public void show() {
        System.out.println("我是 Student");
    }

    public void ncee() {
        System.out.println("高考");
    }

}


class Teacher extends  Person{  // 子类
    int teacherId;

    @Override
    public void show() {
        System.out.println("我是 Teacher");
    }


    public void teach() {
        System.out.println("教书育人");
    }
}

在这里插入图片描述


4. 多态的总结

  1. 多态是运行时类型, 编译看左边, 运行看右边
  2. 抽象类,接口(无法 new 实例对象) 的使用充分体现了多态性, 调用实现的子类中的方法.
  3. 多态中,父类/子类中都含有的同名的变量名, 无论是编译,还是运行的结果 都是看左边的编译赋值的是子类/父类 类型就调用谁中的变量值.因为变量是无法重写的。
  4. 重写中返回类型要必须 <= 父类中被重写的返回类型,重写中的声明的权限类型必须 >= 父类中被重写的声明的权限特殊的(public,private) 。
  5. 方法才可以重写,变量无法重写。

5. 最后:

限于自身水平,其中存在的错误,希望大家给予指教,韩信点兵——多多益善,谢谢大家,江湖再见,后悔有期。

在这里插入图片描述

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Docker安装(centos 7)

安装 以在centos安装为例&#xff0c;主要有以下几个步骤 1、确定你是CentOS7及以上版本 2、卸载旧版本 3、yum安装gcc相关 yum -y install gccyum -y install gcc-c 4、安装需要的软件包 执行如下命令 yum -y install gcc-c 5、设置stable镜像仓库 由于docker外网镜像…

Docker安装canal-admin以及canal-server

一、安装canal-admin可视化管理工具 此处的数据库已经进行了相应的配置&#xff0c;望周知 docker run -it --name canal-admin \ -e spring.datasource.addressxxx:3306 \ -e spring.datasource.databasecanal_manager \ -e spring.datasource.usernameroot \ -e spring.da…

minicom发送AT指令

参考&#xff1a;使用minicom发AT指令&#xff0c;和外设传感器通信 地址&#xff1a;https://blog.csdn.net/hannibaychty/article/details/125463268 目录1、Linux minicom 和 windows串口调试助手的区别2、使用的基本流程3、使用 minicom 需要注意的几点ARM板子外接传感器&a…

从国企到进大厂,全靠阿里、腾讯内网22版Java核心手册合集

记得19年初的时候&#xff0c;我通过一整天的笔试及面试加入一家(某一线城市国资委全资控股)某集团的研究机构(中央研究院)&#xff0c;任职高级软件工程师(中级职称);在这边工作了整整一年&#xff0c;目前已经跳槽到一家互联网公司&#xff0c;在回头看看这一整年&#xff0c…

SAP如何删除一个已经释放的请求 (SE38 : RDDIT076)

原文链接&#xff1a;https://zhuanlan.zhihu.com/p/425479956 当你搜到这个文章的时候&#xff0c;说明你做了羞羞的事情哦&#xff5e;&#xff08;或者正在准备做羞羞的事情&#xff09;。 此处声明&#xff1a;本帖仅进行可操作性和纯技术讨论&#xff0c;由此造成的一切后…

MMSegmentation使用记录

一、官网下载文件&#xff1a; 当前最新版本为1.0.0rc2 https://github.com/open-mmlab/mmsegmentation/releases/tag/v1.0.0rc2 下载源码解压文件可得到最新版的代码 二、配置环境&#xff1a; 这部分省略&#xff1a;按照给的README文件很快就配置好了 https://github.…

Vuetify中的v-pagination如何实现分页

大家好&#xff0c;我是雄雄。 前言 昨天在改一个系统的时候遇到了个技能点&#xff0c;观察解决了好久&#xff0c;终于解决了&#xff0c;趁热打铁&#xff0c;今天来记录一下。 这个系统是个个人博客&#xff0c;目前我也在使用&#xff0c;但是有个地方用的很不舒服。就是…

32 CPP多态

注意: 1 只需要在基类的函数声明中加上virtual关键字&#xff0c;函数定义时不能加&#xff1b; 2 在派生类中重定义虚函数时&#xff0c;函数特征要相同&#xff1b; 3 当在基类中定义了虚函数时&#xff0c;如果派生类没有重定义该函数&#xff0c;那么将使用基类的虚函数…

设计模式原则 - 接口隔离原则(二)

接口隔离原则一 官方定义二 案例演示普通方案案例分析解决方案解决方案案例总结三 与单一职责原则对比一 官方定义 接口隔离原则&#xff08;Interface Segregation Principle&#xff09;&#xff0c;又称为ISP原则&#xff0c;官方定义为&#xff1a; Clients should not be…

docker安装seata单节点的详细教程

一、环境部署 1、在自己的数据库新建seata数据库 2、利用seata官方提供的seata数据库sql脚本创建所需数据库seata以及表&#xff0c;脚本地址如下&#xff1a; seata/mysql.sql at 1.4.1 seata/seata GitHub 3、查看docker官方镜像仓库版本 4、拉取seata安装镜像 docker…

【机器学习实战】基于代价敏感学习的AdaCost方法用于信用卡欺诈检测

1. 数据集 数据集地址&#xff1a;Credit Card Fraud Detection 数据集整体浏览&#xff1a; 284807个样本&#xff0c;30个特征&#xff0c;1个分类标签Class Class为0的是多数类&#xff0c;一共有284315个样本。 Class为1的是少数类&#xff0c;一共有492个样本&#xff…

写给Python社群的第11课:Python线程,进程,协程,3个毫无关系的兄弟

文章目录⛳️ 线程、进程与协程&#x1f525; 进程与线程简介⛳️ Python 多线程模块&#x1f525; threading 模块&#x1f525; threading 模块实践⛳️ Python 并发进程模块&#x1f525; Process 创建多进程⛳️ 线程、进程与协程 线程、进程、协程 这三个名称相似的概念&…

大话设计模型 Task03:工厂、制造、观察

目录一、建造者模式问题描述问题分析模式定义代码实现二、观察者模式问题描述问题分析模式定义代码实现一、建造者模式 问题描述 我的要求是你用程序画一个小人&#xff0c;这在游戏程序里非常常见&#xff0c;现在简单一点&#xff0c;要求是小人要有头、身体、两手、两脚就可…

左偏树原理详解

一 点睛 左偏树&#xff08;leftist tree 或 leftist heap&#xff09;也叫作左偏堆、左倾堆、左式堆&#xff0c;是计算机科学中的一种树&#xff0c;也是一种优先队列实现方式&#xff0c;属于可并堆&#xff0c;在信息学中十分常见&#xff0c;在统计、最值、模拟、贪心等类…

Win11安装WSL2和Nvidia驱动(2022-12-19)

文章目录前言系统环境WSL 1和WSL 2功能对比安装WSL2更新和升级包配置VSCode配置GPU加速安装Nvidia驱动安装Cuda Toolkit通过PyTorch安装CUDA Toolkit测试Nvcc参考链接前言 以前捣鼓过wsl&#xff0c;即Windows下的Linux子系统&#xff0c;但兼容性依然比不过原生的Linux系统&a…

【Linux】进程间通信之管道

目录&#x1f308;前言&#x1f338;1、IPC介绍&#x1f362;1.1、进程间通信的目的&#x1f361;1.2、背景和发展&#x1f360;1.3、进程间通信的分类&#x1f337;2、管道&#x1f361;2.1、概念&#x1f362;2.2、管道的原理&#x1f363;2.3、匿名管道&#x1f364;2.4、管…

DOM算法系列004-判断给定节点是否为body元素

UID: 20221218221939 aliases: tags: source: cssclass: created: 2022-12-18 如果我们要判定给定的一个节点是不是页面body与元素节点&#xff0c;该如何判断呢&#xff1f; 一般来说&#xff0c; 一个HTML页面内只有一个body元素 但是&#xff0c;如果我们非要在页面内写超过…

Spring boot 整合 redis

Spring boot 整合 redis一、Spring boot 整合redis1.1 启动redis1.2 redis desktop manager1.3 常用命令二、操作2.1 依赖包2.2 配置2.3 简单测试2.4 StringRedisTemplate一、Spring boot 整合redis 1.1 启动redis 命令行启动 redis-server redis-cli1.2 redis desktop mana…

基于electronbot作品bootLoader设计

文章目录 前言 一、芯片程序区规划和流程 1、flash区规划 2、两区运行流程 3、bootLoader代码体现 4、electronbot代码体现&#xff1a; 二、bootLoader代码设计 1.下载程序步骤 2.通讯协议格式 三、libusb开发及需要注意的事情 1、bootLoader复合设备 2、electronbot复合设备…

基础算法系列--[基本数据结构KMP]

文章目录前言链表单链表双链表栈和队列栈队列单调KMP前言 今天要搞的是基本的一些数据结构&#xff0c;当然咱们这个不是那么“正经”。当然今天也没啥代码&#xff0c;因为太简单了&#xff08;其实我也想水一下~&#xff09; 链表 单链表 单链表这个东西&#xff0c;应该…