Java类加载

6 篇文章 0 订阅
文章通过一个类初始化的示例代码,探讨了Java虚拟机(JVM)在加载类时对静态变量的处理过程,包括加载、验证、准备、解析和初始化五个阶段。在准备阶段,静态变量s1和s2被分配并初始化为0,但在解析阶段,由于构造方法的执行,s1和s2分别变为1和3。文章强调了对JVM深入理解的重要性,并指出初始化阶段按代码顺序执行赋值语句和static块。
摘要由CSDN通过智能技术生成

一、引言

        最近社区讨论了一个比较有趣的类初始化代码

public class Test2 {

    public Test2() {
        s1++;
        s2++;
    }
    private static Test2 test2 = new Test2();
    private static int s1;
    private static int s2 = 3;
    public static void main(String[] args) {
        System.out.println(test2.s1);
        System.out.println(test2.s2);
    }

}

        s1和s2的结果是什么,大部分人不知道结果,少部分人将构造方法的执行与静态变量分配的时机进行比较,得到不太确定的结果,只有极少部分对于类初始化研究过的人才能从原理上说出结果。

        这里先看一下结果,那么为什么呢,这里涉及的知识其实非常底层,只依靠java知识和开发经验是不行的,必须对jvm有深入的了解才能分析清楚。

 

二、分析

        想要理解这个例子,那就需要了解一下一个类在初始化过程中,该对象属性变量的分配与初始化过程,这里从《深入理解java虚拟机》第七章进行分析

        在《深入理解java虚拟机》第七章中介绍类加载实际上分为、记载、验证、准备、解析、初始化等五个阶段:

        1、加载:获取类的二进制字节流并且将其转化为方法区的运行时数据结构

        2、验证:保证类的信息符合jvm规范,在编译期提前暴露错误

        可以分为文件格式、源数据、字节码、符号引用等验证

        3、准备:为类静态变量分配内存并且设置初始值

        也就是在这一步可以分析出s1、s2都会分配为0,这是在正常情况下,如果静态变量的字段属性表存在ConstantValue属性那就不一样了,比如下面这行代码

private static final int s3 = 666;
    s3的分配值是666

         4、解析:jvm会把常量池的符号引用替换为直接引用,这个看起来会让许多人不明所以,因为符号引用是Class文件格式使用的,在《深入理解java虚拟机》第六章有详细描述,没有接触过这方面知识的是完全不能理解甚至歪曲理解的。

        而直接引用大家就知道了,是指针、句柄、或者相对偏移量。

        解析又分为类、接口、字段、方法、接口方法解析,这里就是构造方法被解析的阶段,此时s1 = 1,s2 = 1

public Test2() {
        s1++;
        s2++;
    }

        5、初始化:这是类加载的最后一个阶段,也是程序员编写的代码开始执行逻辑的开始,换一种理解,初始化就是执行类构造器<clinit>,<clinit>是javac编译器自动生成的。

        <clinit>收集类变量的赋值动作和静态语句合并成,静态语句就是static{}块,这里是赋值语句执行的阶段,因此最终s1 = 1,s2= 3

private static int s2 = 3;

        6、额外分析

        这段代码没有涉及static块,如果代码是

public class Test2 {

    public Test2() {
        s1++;
        s2++;
    }
    static {
        s2 = 0;
    }
    private static Test2 test2 = new Test2();
    private static int s1;
    private static int s2 = 3;
    public static void main(String[] args) {
        System.out.println(test2.s1);
        System.out.println(test2.s2);
    }

}

        那么s1和s2分别是什么值,答案还是

        如果再做一下改动呢

public class Test2 {

    public Test2() {
        s1++;
        s2++;
    }
    private static Test2 test2 = new Test2();
    private static int s1;
    private static int s2 = 3;
    static {
        s2 = 0;
    }
    public static void main(String[] args) {
        System.out.println(test2.s1);
        System.out.println(test2.s2);
    }

}

        最终是

        可能有的同学已经猜到了,在初始化阶段对于赋值语句和static块是按照代码顺序进行编译运行的。 

三、总结

        看似简单的代码,实际上包含了许多知识,所以提倡多阅读源码和权威书籍,对于代码的执行要有原理上的理解,而不仅仅是开发和记住实践结果。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

胖当当技术

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值