Java 字节码分析成员变量初始化顺序

Java面向对象(高级)-- 类中属性赋值的位置及过程 阅读详情

一、无继承关系

class Demo {
    private int p1 = 100; 	// 实例变量显式初始化
    private static int p2 = 10; 	// 静态变量显式初始化

    {
        p1 = 101; // 构造代码块初始化
    }

    static {
        p2 = 11; // 静态代码块初始化
    }

    public Parent() {  // 构造函数初始化
        this.p1 = 102;
        this.p2 = 12;
    }

}

1、静态变量

上述代码中静态变量 p2 赋值流程:

(1)p2 = 0 :类加载的准备阶段,静态变量设置默认零值。
(2)p2 = 10 —> p2 = 11:类加载的初始化阶段,执行 clinit()。

clinit() 由编译器按语句在源文件中出现的顺序自动收集类中的类变量的赋值操作和静态语句块中的语句合并而成的

(3)p2 = 12 :类对象执行 init() 函数,包括显式初始化,构造代码块和构造函数。

总结静态变量赋值顺序

类加载的准备阶段静态变量初始零值 —> 类加载的初始化阶段静态变量显式赋值 —> 类加载的初始化阶段静态代码块赋值 —> 构造函数赋值

2、实例变量

上述代码中实例变量 p2 赋值流程:

(1)p1 = 0:类对象在堆空间分配,实例字段设置默认零值
(2)p1 = 100 —> p1 = 101 —> p1 = 102:类对象执行 init 函数,包括显式初始化,构造代码块和构造函数

总结实例变量赋值顺序

实例变量默认零值 —> 显式初始化 —> 构造代码块赋值 —> 构造函数赋值

3、整体流程

上述代码中实例变量 p1 与静态变量 p2 整体赋值流程:

(1) p2 = 0 :父类加载的准备阶段,静态变量设置默认零值
(2)p2 = 10 —> p2 = 11:父类加载的初始化阶段,执行 clinit()
(3)p1 = 0:子类对象在堆空间分配,实例字段设置默认零值
(4)p1 = 100 —> p1 = 101 —> p1 = 102,p2 = 12 :类对象执行 init() 函数,包括显式初始化,构造代码块和构造函数

总结变量赋值顺序

类加载的准备阶段静态变量初始零值 —> 类加载的初始化阶段静态变量显式赋值 —> 类加载的初始化阶段静态代码块赋值 —> 实例变量默认零值 —> 实例变量显式初始化 —> 实例变量构造代码块赋值-> 构造函数给静态变量、实例变量赋值

二、继承中的变量赋值

class Parent {
    private int p1 = 100; //实例变量显式初始化
    private static int p2 = 10; //静态变量显式初始化

    {
        p1 = 101; //构造代码块初始化
    }

    static {
        p2 = 11; //静态代码块初始化
    }

    public Parent() {  //构造函数初始化
        this.p1 = 102;
        this.p2 = 12;
    }

}

public class Son extends Parent{

    private int s1 = 100;
    private static int s2 = 10;

    {
        s1 = 101;
    }

    static {
        s2 = 11;
    }

    public Son() {
        this.s1 = 102;
        this.s2 = 12;
    }

    public static void main(String[] args) {
        Son son = new Son();
    }
}

上述代码变量赋值流程:

(1)p2 = 0 :父类加载的准备阶段,静态变量设置默认零值
(2)p2 = 10 , p2 = 11:父类加载的初始化阶段,执行clinit()
(3)s2 = 0 :子类加载的准备阶段,静态变量设置默认零值
(4)s2 = 10 , s2 = 11:子类加载的初始化阶段,执行clinit()
(5)p1 = 0,s1 = 0 :子类对象在堆空间分配,实例字段设置默认零值,这里实例字段也包括从父类中继承来的字段
(6)p1 = 100, p1 = 101, p1 = 102,p2 = 12 :父类对象执行 init() 函数,包括显式初始化,构造代码块和构造函数
(7)s1 = 100, s1 = 101, s1 = 102,s2 = 12 :子类对象执行 init() 函数,包括显式初始化,构造代码块和构造函数

上述代码字节码分析:

从字节码角度来分析,对 class 文件进行反编译后,Son son = new Son() 会转化为如下三条字节码指令:

0 new #4 <Son> 
3 dup 
4 invokespecial #5 <Son.<init>> 

其中 new 指令会执行如下操作:首先判断该对象的类是否加载,如果没有加载,就进行加载。类加载的过程可以细分为加载、验证、准备和初始化5个阶段。其中准备阶段会给静态变量分配默认零值,所以静态变量就算没有显式也是可以使用的。在初始化阶段,会执行 clinit() 方法,这个方法没有在 java 代码中定义,但是在 java 代码编译时,jvm 会给每个类自动生成一个 clinit() 方法,该方法会自动收集类中所有静态变量的赋值动作和静态代码块中的语句并执行,下面就是 son 类中 clinit() 方法的字节码指令:

0 bipush 10  
2 putstatic #3 <Son.s2>  
5 bipush 11  
7 putstatic #3 <Son.s2> 
10 return 

我们可以看到 clinit 方法中,会先将 10 压入操作数栈,然后赋值给静态变量 s2,接着将 11 压入操作数栈,并赋值给静态变量 s2,也就是 clinit 方法先执行显式赋值动作,然后执行 static 代码块中的动作。

还有一个重要的点就是在类加载的过程中,如果该类存在父类,就会先加载父类,然后加载子类,因此会先初始化父类静态变量,然后初始化子类静态变量。

完成类加载后,new 指令就会在堆中分配一块内存空间给实例对象,虚拟机会将实例字段都初始化为零值,这一步操作保证了对象的实例字段在java代码中可以不赋初值就可以直接访问,程序能访问到这些字段的数据类型所对应的零值。这里的实例字段也包括从父类中继承下来的字段。

完成new指令后,进行dup指令,就是复制一份新建对象的引用到操作数栈中。然后执行 invokespecial,该指令会调用 Son.init() 方法,我们现在来看 Son.init() 的字节码:

0 aload_0  
1 invokespecial #1 <Parent.<init>>  
4 aload_0  
5 bipush 100  
7 putfield #2 <Son.s1> 
10 aload_0 
11 bipush 101 
13 putfield #2 <Son.s1> 
16 aload_0 
17 bipush 102 
19 putfield #2 <Son.s1>
22 aload_0 
23 pop 
24 bipush 12 
26 putstatic #3 <Son.s2> 
29 return 

Son.init() 方法中可以看到,它会首先调用父类的 init() 方法,然后对子类实例变量进行显示赋值,接着执行构造代码块中的赋值动作,最后进行构造函数中的赋值动作,所以这里 s1 的变化就是:刚开始为为默认零值,然后显式赋值为 101,接接构造代码块将 s1 赋值为 101,最后在构造函数中将 s1 赋值为 102,s2 赋值为 12。

总结变量赋值顺序

父类静态变量初始零值 -> 父类静态变量显式赋值 -> 父类静态代码块赋值 -> 子类静态变量初始零值 -> 子类静态变量显式赋值 -> 子类静态代码块赋值 -> 实例变量默认零值 -> 父类构造代码块赋值 -> 父类构造函数赋值 -> 子类构造代码块赋值 -> 子类构造函数赋值

关于Java静态成员变量和静态初始化块等的初始化顺序的详细介绍 详细介绍了Java的静态成员变量、静态数据块、非静态成员变量和非静态成员变量等初始化顺序 立即下载

相关推荐

SIMPLIS实战:从零构建理想BUCK电路,精准计算与POP稳态分析

本文详细介绍了如何使用SIMPLIS软件从零开始构建并仿真一个理想的BUCK降压电路。通过设定关键参数、计算理想器件值,并搭建仿真模型,文章重点演示了暂态仿真以观察电路上电过程,并深入讲解了高效的POP(周期性工作点)稳态分析技术,帮助工程师快速验证设计、评估稳态性能,从而加速电源设计迭代。

weixin_29165465的博客 390

JVM之类加载阶段详解

JVM之类加载阶段详解类加载阶段总览加载获取二进制流将字节流转换为运行时数据结构堆中生成Class对象特殊连接验证准备解析名词解释何时进行解析哪些类型初始化使用卸载 类加载阶段总览 注意:这些阶段的顺序虽然是确定的,但是这些阶段通常都是互相交叉混合进行的,会在一个阶段中调用,激活另外一个阶段执行 加载 加载阶段顾名思义,也就是Class文件所代表的的 类/接口 被加载到虚拟机中。 那么什么时候(类加载的时机), 谁来加载(哪个类加载器),将哪个Class文件(Class的名称是什么)到虚拟机中呢? 1

北洋的博客 7984

[Pytorch案例实践001]手写MNIST数字识别

Pytorch实现手写数字识别,带完整数据集。

2403_83044722的博客 1114

关于Java子类与父类定义变量赋值先后

首先我要写个代码然后用复制文档这里有个直接对变量进行函数赋值然后进行关于变量的探究 赋值首先是对子类构造函数赋值对super开始操作。 过来后对父类的变量进行初始化 然后就对父类的构造函数进行赋值 这里就开始对子类的变量进行初始化 这里就是对子类构造函数的赋值了 而这里子类initbrand里面的brand晚于父类brand所以父类会接受到null ...

gf1136010396的博客 1250

消费级显卡跑大模型:RTX 3060 12G 部署 70 亿参数 AI 编程助手的优化实战

本文详细介绍了如何在消费级显卡RTX 3060 12G上,通过量化、投机解码和分页KV-Cache等核心优化技术,成功部署并流畅运行70亿参数的CodeLlama-7B编程助手。文章提供了从环境配置、模型准备到参数调优的完整实战笔记,实测显存占用降至7.3GB,生成速度达21.7 token/秒,为开发者和技术爱好者提供了可行的本地AI部署方案。

weixin_29053067的博客 1251

寻路最终版哦

相信大家用unity3d寻路的时候,一定会碰到多人

yxriyin的专栏 6953

Java初始化顺序

csdn上有好多关于Java初始化顺序的口诀什么的,示例代码也有,但是一股脑儿地把这些灌输给读者,需要读者以极大的精力去深入理解。下面我将以几个例子逐步给大家讲明白其中的构造顺序。看到这大家应该对上三个相应部分的初始化都有了自己的理解。1)按照成员变量定义顺序进行初始化。即使变量定义散布于方法定义中,他们依然在任意方法被调用前进行初始化2)静态对象(变量)优先于非静态变量对象初始化,其中,静态对象(变量)只初始化一次,非静态对象可能多次初始化;3)父类优先于子类进行初始化;

m0_73815403的博客 1720

【AI黑话日日新】什么是AI Infra?

是支撑AI模型开发、训练、推理、部署与规模化运营的,是连接底层算力与上层AI应用的“数字基建”。没有高效的AI Infra,大模型训练、推理与落地都无法高效实现。

专注:太空算力、AI infra、异构调度、大模型成本工程 1094

java 成员变量什么时候初始化_Java中成员变量的初始化过程

Java中的成员变量分为两种:第一种是成员变量,第二种是局部变量。成员变量的作用范围是整个类,局部变量的作用范围是该变量所在的大括号内。变量可以被static修饰,也可以放在不同的地方被初始化,而这些因素都会影响到变量的初始化值和被初始化的顺序。1.没有被修饰符修饰的成员变量和局部变量的初始化在java中所有的成员变量都会被系统赋予默认是初始值,这个初始值根据不同的数据类型有着不同的定义:Bool...

weixin_33204522的博客 3821

【人工智能】广度优先搜索(BFS)在人工智能中的应用(Python源码)

本文系统阐述了广度优先搜索(BFS)算法在人工智能领域的核心原理与应用。作为无信息搜索算法,BFS通过FIFO队列实现节点遍历,保证在无权图中的最优性。文章详细解析了BFS的伪代码实现和Python编程示例,并通过机器人路径规划案例展示了其实际应用场景。测试结果表明,BFS能有效解决包含障碍物的网格寻路问题,验证了算法在AI领域的实用价值。该研究为理解基础搜索算法及其工程应用提供了重要参考。

梦想起飞的地方 1141

【2023校招】网易雷火_研发岗_笔试题解

几个月没写题,果然水平不在线了hh,AK更难实现喽,当天只AK了疯狂游戏; 题目描述和数据等尽量多的CV到了DEVC里,可能不够细节和准确; 下面进入正题,分享我们此次笔试的题目描述和代码设计吧~.........

米莱虾 4543

java byte 初始化_Java成员变量初始化顺序完整版(附字节码分析)

先说结论:静态变量初始零值 -> 静态变量显式赋值 -> 静态代码块赋值 -> 实例变量默认零值 -> 构造代码块赋值 -> 构造函数赋值如果考虑到父类,其初始化顺序为:父类静态变量初始零值 -> 父类静态变量显式赋值 -> 父类静态代码块赋值 -> 子类静态变量初始零值 -> 子类静态变量显式赋值 -> 子类静态代码块赋值 ->...

weixin_39800990的博客 763

19、时间触发通信与时间触发以太网技术解析

本文深入解析了时间触发通信和时间触发以太网(TTEthernet)技术,涵盖了其在实时系统中的应用,特别是安全关键领域如航空航天。文章详细介绍了TTEthernet的通信服务、协议参数化、时钟同步机制、诊断服务、故障隔离方法以及验证工作,并通过示例配置展示了其灵活性和可扩展性。TTEthernet通过引入确定性时间触发通信范式,在同一物理网络上实现了时间触发、速率受限和尽力而为三种流量类别的集成,为分布式实时系统提供了高可靠性和实时性保障。

efc12345678的博客 115

【Kotlin】从字节码角度理解kotlin构造函数、成员变量、init代码块执行顺序

kotlin的构造函数、init代码块、成员变量初始化三者执行顺序是怎样的? 这篇文档从节字码的角度去理解它们的逻辑。

devnn的专栏 1387

jre9下载 jdk9

(Java SE Runtime Environment 9.0) 9.0u175 官方最新版 【x86|x64】

怀旧版电脑GBC模拟器

再现原版GBC掌机的界面,电脑运行连游戏界面都一样大小,很怀旧,很真实,(英文版)强烈推荐,很小的文件,很难才找到的。

jvm 初始化之 <cinit> , <init>

public class T2{ static{ System.out.println("-----.static---"); } static T2 t2 = new T2(); C t =new C(); static String str = "ff"; public T2(){System.out.println("-----....

xiaomin1991222的专栏 272

pscad IEEE标准系统搭建说明

pscad IEEE标准系统搭建说明,包含典型的IEEE 9节点、14节点、30节点、39节点和118节点系统。内含标准测试系统的原始数据和潮流结果。

通过Java字节码发现有趣的内幕之初始化篇(三)

关于类初始化过程网上有很多相关的文章,其实也算是学习语言时一个基础知识,但今天我想从字节码表现上更深入的来理解各种场景下的实例初始化过程是怎么样的,从简单到复杂大体分为下面几个场景。1、成员+构造函数2、成员+代码块+构造函数3、静态变量+静态代码块4、继承和多态首先明确下运行环境:我们先来看一下第一个场景:成员+构造函数,也是我们最经常使用到的场景。在这个场景中想通过字节码...

weixin_41149739的博客 759

VC中sqlite的使用

在VC中对sqlite的使用,实现数据库大批数据的快速插入,读取数据库中的数据在ListCtrl中显示

Java子类和父类的初始化执行顺序

要明白子类和父类的初始化执行顺序,只需要知晓以下三点,就不会再弄错了。1.创建子类对象时,子类和父类的静态块和构造方法的执行顺序为:父类静态块->子类静态块->父类构造器->子类构造器。深入理解为什么是这个顺序,可以看我这篇文章:从京东面试题看java类和对象的初始化2.静态变量的声明和赋值,声明会在静态块之前,赋值运算将会合并到静态块中,顺序和源代码中的顺序一致。举例如下:源代...

黑马程序员广州中心的专栏 324

Java -- 父类和子类拥有同名变量

Java – 父类和子类拥有同名变量                       请仔细观察系列两段程序的不同之处:一个Son类重写print方法,另一个则没有重写。 分析该问题必须从字节码的角度入手,否则根本不可能看清事情的本质。 成员变量(非静态的)的赋值过程: 默认初始化 显示初始化

Syntactic Sugar 4432

JVM 学习笔记十七、字节码与执行细节

十七、字节码与执行细节 1、字节码文件的跨平台性 Java语言:跨平台的语言(write once ,run anywhere) 当Java源代码成功编译成字节码后,如果想在不同的平台上面运行,则无须再次编译 Java虚拟机:跨语言的平台 Java虚拟机不和包括 Java 在内的任何语言绑定,它只与“Class 文件”这种特定的二进制文件格式所关联。 无论使用何种语言进行软件开发,只要能将源文件编译为正确的Class文件,那么这种语言就可以在Java虚拟机上执行。 想要让一个Java程序正确地运.

小宇哥x的博客 234

字节码文件简介

目录 一、概述 二、透过字节码查看代码执行细节之Integer判断相等 三、透过字节码查看代码执行细节之String判断相等 四、透过字节码查看代码执行细节之类初始化 一、概述 想要让一个Java程序正确地运行在JVM中,Java源码就必须要被编译为符合JVM规范的字节码。 前端编译器的主要任务就是负责将符合Java语法规范的Java代码转换为符合JVM规范的字节码文件。 javac是一种能够将Java源码编译为字节码的前端编译器。 Javac编译器在将Java源码编译为一个有效的字节码文

weixiaohuai的博客 1860

Java 重写方法与初始化的隐患

虽然文章标题是Java, 但几乎所有面向对象设计的语言都遵守这个初始化流程, 感谢廖祜秋liaohuqiu_秋百万指出, 之前忘记提这个了. 前言 drakeet写了个和RecyclerView相关的GenerousRecyclerView, 原文提到了写这个的目的. 因为需要知道ViewGroup的clipToPadding属性, 所以调用了View...

weixin_34228387的博客 211

构造方法与静态代码块字节码指令详解

在上一次【https://www.cnblogs.com/webor2006/p/9630895.html】中已经花了很大的篇幅完整的将复杂文件中的常量相关的信息给分析完了,这次来将剩下的字节相关的信息给分析完,那接下来该分析啥了呢?先来整体回顾一下Java字节码的整体结构: 下面则一个个接着进行分析: Access Flags:访问标记 往后数2个字节: 那它具体代表啥含义呢,此时就又...

webor2006的博客 139

构造方法与方法调用指令分析 || JVM类加载与字节码技术

编译器会按从上至下的顺序,收集所有 {} 代码块和成员变量赋值的代码,形成新的构造方法,但原始构造方法内的代码总是在最后。最终方法(final),私有方法(private),构造方法都是由 invokespecial 指令来调用,属于静。编译器会按从上至下的顺序,收集所有 static 静态代码块和静态成员赋值的代码,合并为一个特殊的方法。new #2 : 是创建【对象】,给对象分配堆内存,执行成功会将【对象引用】压入操作数栈。成员方法与静态方法调用的另一个区别是,执行方法前是否需要【对象引用】

兀坐晴窗独饮茶 658

17.1 class文件结构 - 概述

Java虚拟机:跨语言的平台 Java虚拟机不和包括Java在内的任何语言绑定,它只与“Class文件”这种特定的二进制文件格式所关联。无论使用何种语言进行软件开发,只要能将源文件编译为正确的Class文件,那么这种语言就可以在Java虚拟机上执行。可以说,统一而强大的Class文件结构,就是Java虚拟机的基石、 桥梁。想要让一个Java程序正确地运行在JVM中, Java源码就必须要被编译为符合JVM规范的字节码。前端编译器的主要任务就是负责将符合Java语法规范的Java代码转换为符合JVM规范的字节

u013559614的博客 200

非静态成员变量的加载过程

代码: class Father { int x = 10; public Father() { this.print(); x = 20; } public void print() { System.out.println("Father.x = " + x); } } class Son extends Father { int x = 30; public Son() {

qq_42449963的博客 410
上一篇: Java 线程池
下一篇: Java 继承中的属性隐藏与方法重写
IT__learning
博客等级 码龄7年 1107粉丝 148原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值