类加载机制中的【初始化】步骤解析

前言

我们知道系统加载.class文件分别有这几个步骤:加载连接初始化三个阶段。

其中加载阶段会通过全类名获得到对应文件的二进制字节流,再通过native方法产生一个Class对象,这一步主要由类加载器来完成。也就是说Class对象在第一步就产生了。

连接阶段又分为验证准备解析三个步骤。其中JVM会在准备阶段为类的所有静态变量赋上初值。

最后的初始化阶段,JVM会执行在编译时(javac命令生成.class时)生成的<clinit> () 方法,这个方法是用来初始化所有类的静态变量的,也就是说类的静态变量在这里才真正的初始化,之前的准备阶段并不会初始化静态变量,只是对静态变量赋初始值,比如int类型赋值为0,具体的值需要要初始化阶段才会赋上。

但是静态常量,即被final修饰的变量,会在准备阶段就进行初始化操作。

初始化步骤分析

根据上面的结论,我们知道初始化阶段其实就是初始化类静态变量的时期。

我们知道类名.classClass.forName("类名")都能得到对应类的Class对象,但是前者不会进行初始化操作,而后者会进行初始化操作。现在来看一个例子,体会这个差异:

import org.junit.Test;

public class MyTest {

    @Test
    public void test1(){
        System.out.println(Apple.class);
    }
    @Test
    public void test2() throws ClassNotFoundException {
        System.out.println(Class.forName("Apple"));
    }
}

class Apple {
    static {
        System.out.println("static init");
    }
}

test1输出如下:

test2输出如下:

可以发现类名.class由于不会进行初始化步骤,确实没有执行<cinit>()方法,走静态代码块。

Class.forName(String name, boolean initialize, ClassLoader loader)方法参数分析

我们知道Class.forName()方法其实还有一个重载方法,可以传入一个布尔值和一个ClassLoader,其实这个布尔值就是告诉JVM是否进行初始化步骤

在刚才的类中再添加test3:

@Test
    public void test3() throws ClassNotFoundException {
        System.out.println(Class.forName("Apple", false, Apple.class.getClassLoader()));
    }

执行该方法,输出如下:

注意到由于设置了false,这次forName方法并没有对Apple类进行初始化操作。

Main方法的初始化

其实如果是main()方法所在类的话,还有一些小细节值得注意。

假设有如下代码:

public class MyTest {
    public static void main(String[] args) throws ClassNotFoundException {
    }
    public Apple apple(){
        return new Apple();
    }
}

运行main()方法,即使里面没有任何代码,如果我们在运行前把路径下的Apple.class文件删除,那么就会报如下错误:

可以注意到即使main()中不运行任何代码,但是也会去加载其所在类MyTest中引用的所有类,这里就是Apple类。并且这里的加载也不会执行初始化步骤

修改上面代码如下:

public class MyTest {
    public static void main(String[] args) throws ClassNotFoundException {
    	new Banana();
    }
}
class Banana{
    public Apple apple(){
        return new Apple();
    }
}

在运行前把路径下的Apple.class文件删除,发现不会报任何错误,程序正常退出。

由此可以得出JVM在运行main()所在类时,会将该类引用的所有类型的Class对象全部加载进内存。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Java加载初始化Java虚拟机(JVM)执行的一部分。加载是将的.class文件从磁盘加载JVM的过程,而初始化是为的静态变量赋初值并执行静态块的过程。 加载过程分为以下三个步骤: 1.加载(loading):将的.class文件从磁盘读入内存,并为之创建一个Class对象。 2.链接(linking):将的二进制代码合并到JVM的运行状态,并分配内存空间。链接过程又分为三个子步骤: - 验证(verification):验证的二进制代码是否符合JVM规范,包括语法检查、型检查、字节码验证等。 - 准备(preparation):为静态变量分配内存空间并赋初值(默认值),如int型为0,对象型为null。 - 解析(resolution):将符号引用转换为直接引用,如将名转换为的地址引用。 3.初始化(initialization):为的静态变量赋实际值,并执行静态块。 初始化规则: - 当创建的实例时,如果该还没有被初始化,则先初始化。 - 当访问的静态变量或静态方法时,如果该还没有被初始化,则先初始化。 - 当子初始化时,如果发现父还没有被初始化,则先初始化。 - 当虚拟机启动时,初始化所有被引用的。 总之,加载初始化Java虚拟机非常重要的概念,对理解Java的运行机制有很大帮助。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值