【Java】继承背后那些事---深扒继承基本原理|类加载、子类对象创建、方法调用、变量访问

news2024/11/28 7:33:06

在这里插入图片描述

  • 博主简介:努力学习的预备程序媛一枚~
  • 博主主页: @是瑶瑶子啦
  • 所属专栏: Java岛冒险记【从小白到大佬之路】

在这里插入图片描述
学习了继承、多态
本节,将通过一个简单的例子,从概念上介绍原理(实际实现的细节与此有所差别),更好的清晰明了的掌握继承!

目录

  • Part1:背景:
  • Part2:类加载流程
    • 2.1:类包含的信息
    • 2.2:类加载流程
    • 2.3:内存布局
  • Part3:对象创建过程
    • 3.1:内存分布
  • Part4:实例方法调用
  • Part5:访问属性

Part1:背景:

这里我们用两个类来演示一下:

Inheritance
Inheritance
Father
+ int count$
- int a
+Father()
#enjoy()
+action()
Child
+ int count$
- int a
+Child()
#enjoy()
Object
public class Father {
    public static int count;
    private int a;

    static {
        System.out.println("Father类的静态初始化代码块被执行");
        count = 1;
    }

    {
        System.out.println("Father类的实例代码块被执行");
        a = 1;
    }

    public Father() {
        System.out.println("Father类的构造器被调用");
        a = 2;
    }

    protected void enjoy() {
        System.out.println("enjoy smoking");
    }

    public void action() {
        System.out.println("Start");
        enjoy();
        System.out.println("Ended");
    }
}

public class Child extends Father {
    public static int count;
    private int a;

    static {
        System.out.println("Child类的静态代码块被调用");
    }

    {
        System.out.println("Child类的实例代码块被调用");
    }

    public Child() {
        System.out.println("Child类的构造方法被调用");
    }

    @Override
    protected void enjoy() {
        System.out.println("enjoy studying");
    }

}

当我们在测试类中创建子类Child c = new Child(),由于类第一次被使用,类会被加载进内存。那类是怎么样被加载进内存的呢,又把哪些信息加载进了内存哪里?这之间发生了什么,我们接下来将进行系统的讲解。

Part2:类加载流程

2.1:类包含的信息

在类第一次被使用的时候,类会被JVM加载进内存中的方法区。方法区中存储着类的信息,类的信息包括哪些呢?
在这里插入图片描述

2.2:类加载流程

在这里插入图片描述

2.3:内存布局

知道了类在何时加载进内存加载进内存的哪个位置将哪些信息存储加载流程之后,我们来看看此时方法区的内存分别是怎样的。
就我们的例子来说,完整加载流程走完之后,内存会保存三个类的信息,分别是:顶级父类Object、Father、Child
在这里插入图片描述


Part3:对象创建过程

new 对象时,首先第一步是将相应的类及其父类加载进内存,并完成其初始化,第一步在Part2已经讲解完,所有类加载并初始化后内存布局已经给出。我们现在来看一下Child c = new Child()JVM所要做的第二步:在堆内存中创建对象的过程

在这里插入图片描述

3.1:内存分布

在这里插入图片描述

Part4:实例方法调用

关于实例方法调用,实例方法重写和动态绑定,已经在这两篇文章中详细叙述

但是这里就基本原理的角度,再次讲解一遍,使印象深刻

再次强调:静态属性、静态方法和非静态的属性都可以被继承和隐藏(hide),而不能够被重写!也更谈不上动态绑定

Child c = new Child();
f = c;
c.action();
f.action();

首先,在调用方法前,JVM内存分布如下:
在这里插入图片描述

  • c.action()

    • 首先,通过c找到堆内存中对象,发现实际类型信息Child
    • 到Child类型信息中寻找实例方法action()–>没有找到
    • 通过Child类型信息中父类信息引用向上寻找父类类型信息
    • 发现父类中存在相匹配的实例方法action(),并调用。
      • 执行第一条语句 ,System.out.println("Start");输出“Start"
      • 执行第二条语句,调用实例方法enjoy();到实际类型Child中寻找enjoy方法,找到了并且调用
      • 执行第三条语句:System.out,println("End");
  • f.action()(节约时间的话,可以不用看了,和c,action()一模一样)

    • 首先,通过f找到所指向的在堆内存中的对象,实际类型信息为Child
    • 到Child类型信息中寻找实例方法action()–>没有找到
    • 通过Child类型信息中父类信息引用向上寻找父类类型信息
    • 发现父类中存在相匹配的实例方法action(),并调用。
      • 执行第一条语句 ,System.out.println("Start");输出“Start"
      • 执行第二条语句,调用实例方法enjoy();到实际类型Child中寻找enjoy方法,找到了并且调用
      • 执行第三条语句:System.out,println("End");

【总结】

  • 引用在调用对象实例方法时,会找到在堆内存中对象的实际地址(保存了该对象的实际类型信息的引用
  • 然后根据实际类型信息调用实例方法
  • 如果在此类中找不到对应实例方法,将会从实际类型开始,逐级向上(父类)中查找,直到调用到合适实例方法

这里f.action()c.action()所执行的结果完全相同,为什么?因为f和c指向的是堆内存中同一对象,同一对象的实际类型唯一。而调用对象实例方法看的就是实际类型,所以自然方法调用所指向的结果相同!

这其实就是动态绑定的实现机制:根据对象的实际类型调用实例方法,在实际类型中找不到,就逐级向上(父类)中查找。


【补充】:虚方法表

根据上述讲解,到现在,我们堆方法重写,动态绑定已经非常清楚。

我们看到,在判断调用实例方法时,要做一个操作:向上查找,直到找到。如果继承只有一两层还会,如果继承层次太深,每一次都要进行这种查找,效率比较低。于是为了优化,提出了虚方法表的概念。

📍所谓虚方法表,就是在每个类在创建的时候,为其创建一个表,来记录该类对象所有动态绑定方法(包括从父类继承过来的方法)及其地址。一个方法只有一条,如果该类重写了从父类继承过来的方法,那么该方法记录的就是子类重写之后的那个方法。

所以,在本篇文章的背景下,虚方法表是这样的:
在这里插入图片描述

有了虚方法表,只要我们确定了该对象的实际类型,就可以通过查该类型的虚方法表的方式来直接确定调用哪个实例方法。效率就会提高很多。

Part5:访问属性

我们知道,只有方法才谈动态绑定,属性是不存在什么动态绑定的。一下理解都是正确的:

  • 属性的访问看编译类型
  • 访问属性是静态绑定,无论是否为静态!

访问过程:

  • 首先,由于静态绑定的存在,查看堆内存中该编译类型对应的实例变量(该对象的堆内存中存在父类的实例变量和本类的实例变量)
  • 若存在,则直接访问
  • 本编译类型不存在,则从本编译类型开始逐级向上查找,直到某个父类中存在同名属性且可以访问,则访问。(查找关系

在这里插入图片描述

  • Java岛冒险记【从小白到大佬之路】

  • LeetCode每日一题–进击大厂

  • Go语言核心编程

  • 算法

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

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

相关文章

HuggingGPT解析:使用 ChatGPT及HuggingFace上的族系解决AI问题

HuggingGPT解析:使用 ChatGPT及HuggingFace上的族系解决AI问题 HuggingGPT是一个利用大型语言模型(LLMs)来解决复杂AI任务的框架。其基本理念是,考虑到LLMs(例如ChatGPT)在语言理解、生成、交互和推理方面展现出了卓越的能力&…

一个优质软件测试工程师简历的范文(答应我一定要收藏起来)

很多刚转行软件测试的小伙伴是不是不知道怎么写好一份优质的软件测试工程师的简历。今天呢,就给大家分享一下一个优质软件测试工程师简历的范文。记得收藏起来哦。 下面的案例:2-3年的软件测试工程的简历 姓 名:XXX 学历:本科 …

源码解析Collections.sort ——从一个逃过单测的 bug 说起

源码解析Collections.sort ——从一个逃过单测的 bug 说起 本文从一个小明写的bug 开始,讲bug的发现、排查定位,并由此展开对涉及的算法进行图解分析和源码分析。 事情挺曲折的,因为小明的代码是有单测的,让小明更加笃定自己写的…

第四节 配置SpringBootAdmin日志管理

本来想用一节就写完SpringBootAdmin的,但随着研究的深入发现一节应该是不够的,网上的资料也不会非常系统,官网的例子有些已经好几年没更新了,所以接下来还是系统性的来写下吧 第一节 完成基础配置,暴露所有端点 第二节…

uniapp App强制更新

需要使用DClound插件市场的一个插件挺好用的!app升级、整包更新和热更新组件 支持vue3 支持打开安卓、苹果应用市场,wgt静默更新https://ext.dcloud.net.cn/plugin?id7286 开始贴代码 // /utils/method.js/*** 获取当前app最新版本* param number ver…

【JAVA】这几个JAVA学习网站你绝不能错过(教学课程篇)

个人主页:【😊许思王】 文章目录 前言HOW2J.CNw3cschool菜鸟教程慕课网开课吧黑马程序员B站 前言 JAVA很难学?学不会怎么办?找对学习网站,让你轻松解决困难。 HOW2J.CN HOW2J.CN是我自认为最好的JAVA学习网站&#x…

df -h 查看Used+Avail != Size

问题描述: 在测试过程中发现,该机器的根目录空间 41G 5.7G ! 50G,即 Used Avail ! Size 问题原因: 经过搜索,了解到这种情况可能是Linux系统默认的文件保留块导致的(Linux系统默认保留5%的容量作为应急…

论文 | 一分钟快速找到自己研究领域的顶刊

1. 打开Web of Science https://www.webofscience.com/wos/alldb/basic-search 2. 点击右上角:产品,再选中:Journal Citation Reports 至于JCR是什么,请看下面的拓展部分 3. 单击顶部的 Categories Categories 是分类、类别的…

LaTeX 速查手册

✅作者简介:人工智能专业本科在读,喜欢计算机与编程,写博客记录自己的学习历程。 🍎个人主页:小嗷犬的个人主页 🍊个人网站:小嗷犬的技术小站 🥭个人信条:为天地立心&…

MachineLearningWu_11_NeuralNetworkLayer

x.1 Neural Network layer design 我们接下来来讲解一下深度学习中,神经网络是如何架构的。对于一个具有两层的的神经网络,我们将输入层置为layer0,将第一层隐藏层置为layer1,将 w 2 [ 1 ] w_2^{[1]} w2[1]​标记为第一层中&…

nginx基本使用

这是一份完整的nginx配置文件: #user nobody; worker_processes 1;#error_log logs/error.log; #error_log logs/error.log notice; #error_log logs/error.log info;#pid logs/nginx.pid;events {worker_connections 1024; }http {include mi…

23款奔驰GLS450更换迈巴赫GLS600外观套件,尽显奢华

在外观上不要过分的张扬,低调的同时还要彰显强大的气场,换装迈巴赫专属套件,迈巴赫专属踏板,还有迈巴赫的醒目M标志,车身轮廓和线条方面,奔驰GLS450和迈巴赫GLS600尺寸及其契合,只需通过增加一些…

LayUi基础入门【附有案例从0到1详解】

🥳🥳Welcome Huihuis Code World ! !🥳🥳 接下来看看由辉辉所写的关于LayUi的相关操作吧 目录 🥳🥳Welcome Huihuis Code World ! !🥳🥳 一.LayUi的创作背景 二.LayUi是什么 三.…

请收藏!2023年全栈开发人员实战进阶指南终极版

全栈工程师在过去十年中越来越受到欢迎,而且在国内的就业环境下,它是更适合从技术转管理的职位。 但究竟什么是全栈工程师?他需要哪些技术能力?如何才能成为一名优秀的全栈工程师?今天这篇文章就给大家全方位分享一下…

ES6基础语法

目录 解构 数组解构 对象解构 基本数据解构 对象 对象简写 箭头函数 扩展运算符 函数参数解构 对象API拓展 Object.is() Object.assign() Object.getPrototypeOf() Object.setPrototypeOf() Object.keys() Object.values() Object.entries() Object.fromEntries()…

Java --- 云尚优选项目

目录 一、项目工程搭建 二、配置项目相关maven依赖 2.1、cjc.ssyx父工程依赖 2.2、common模块pom依赖 2.3、common-util模块pom依赖 2.4、service-util模块pom依赖 2.5、model模块pom依赖 2.6、service模块pom依赖 三、编写相关工具类 3.1、mybatis-plus分页查询配置…

SpringMVC学习笔记一

目录 一、SpringMVC概述二、入门案例1.导入相关依赖2.配置web.xml3.配置SpringMVC4.创建测试用的html页面5.编写Controller 三、请求映射规则RequestMapping1.RequestMapping注解标识的位置2.RequestMapping注解value属性3.RequestMapping注解的method属性4.RequestMapping注解…

Vector - CANoe - 测试报告配置

目录 一、测试报告格式设置 二、测试报告格式转换 1、Test Report Viewer format 转换为 PDF 格式

【ELK 企业级日志分析系统】

目录 一、ELK 概述1、ELK 简介1、可以添加的其它组件:2、filebeat 结合 logstash 带来好处: 2、为什么要使用 ELK3、完整日志系统基本特征4、ELK 的工作原理 二、实验操作1、ELK Elasticsearch 集群部署(在Node1、Node2节点上操作&#xff09…

【SpringBoot_Error】关于SpringBoot项目中经常出现yml/xml识别不到的问题

Problems 关于关于SpringBoot项目中经常出现yml/xml识别不到的问题 Solution 在pom.xml文件的<build></build>标签中添加如下代码&#xff1a; > <build><resources><!--检测mapperxml&#xff0c;本项目数据访问层的SQL xml文件放在Java包…