JVM把描述类的数据从Class文件加载到内存,对数据进行校验、转换解析和初始化,最终形成可以被虚拟机直接使用的Java类型,这就是JVM类加载机制。

    Java的类型加载、连接和初始化过程都是在程序运行期间完成的。

    类加载到内存,和到卸载出内存为止,整个生命周期:加载、验证、准备、解析、初始化、使用和卸载,等7个阶段,验证、准备、解析3个部门统称为连接。生命周期如下:

java 类是什么时候加载 java什么时候触发类加载_JVM

对类进行初始化的5种情况:

1.遇到new、getstatic、putstatic、或invokestatic这4条字节码指令时,如果类没有进行过初始化,则需要先触发其初始化。

2.使用java.lang.reflect包的方法对类进行反射调用的时候,如果类么有进行过初始化,则需要先触发其初始化。

3.当初始化类的时候,如果发现其父类还没有初始化,则必须先触发其父类的初始化。

4.虚拟机启动时,用户需要指定一个要执行的主类(包含main()方法的那个类),虚拟机会先初始化这个主类

5.当使用JDK1.7的动态语言支持时,如果一个java.lang.invoke.MethodHandle实例最后的解析结果REF_getStatic、REF_putStatic、REF_invokeStatic的方法句柄,并且这个放啊放句柄所对应的类还没有进行初始化,则需要先触发其初始化。

除了以上5中方式外,主所有的引用类的方法都不会触发初始化,称为被动引用。

package cn.edu.hust.jvm;

public class SuperClass {
    static
    {
        System.out.println("父类被初始化");
    }
    public static int value=123;
}

package cn.edu.hust.jvm;

public class SubClass extends SuperClass{
    static
    {
        System.out.println("子类初始化");
    }
}

package cn.edu.hust.jvm;

public class MainTest {
    public static void main(String[] args)
    {
        System.out.println(SubClass.value);
    }
}

以上代码答应出来的结果是

java 类是什么时候加载 java什么时候触发类加载_初始化_02

这里并没打印出子类初始化等信息,主要的原因是针对于静态字段,只有直接定义这个字段的类才会被初始化。

下面是被动引用的例子:

package cn.edu.hust.jvm;

public class MainTest {
    public static void main(String[] args)
    {
        System.out.println(SubClass.value);
        SubClass[] tt=new SubClass[10];
    }
}

这里为了区分前面的类,使用的是子类创建出数组来查看结果。

java 类是什么时候加载 java什么时候触发类加载_JVM_03

结果也并没有答应出子类初始化等信息,这是因为并没有触发子类的初始化。

但是这里出发了一个继承object类,由newarray创建的类。

被动触发的例子三:

package cn.edu.hust.jvm;

public class Constant {
    static
    {
        System.out.println("constant init");
    }
    public static final String s="hello world";
}

package cn.edu.hust.jvm;

public class MainTest {
    public static void main(String[] args)
    {
//        System.out.println(SubClass.value);
//        SubClass[] tt=new SubClass[10];
        System.out.println(Constant.s);
    }
}

上述例子打印的结果是:

java 类是什么时候加载 java什么时候触发类加载_java 类是什么时候加载_04

这里打印的结果并没有“Constant init”,这是因为符合初始化规则1,final修饰的字段在编译阶段就会存入调用到类的常量池。

也就是说Constan的s字段,将会被存储在MainTest的常量池中。

针对于接口的初始化,需要注意的是:

当一个类在初始化时,要求其父类全部都已经初始化过,但是一个在初始化时,并不要求其父类接口全部都完成初始化,只有在真正使用父借口时,才会初始化。