版权声明:本文为博主原创文章,转载请注明出处,欢迎交流学习!
大家都知道Java程序被编译器编译成字节码文件保存在硬盘里,Java虚拟机在执行代码时首先要把编译后的字节码文件从硬盘加载到内存中,然后才能执行。这里所说的加载包括一连串的动作,即虚拟机把字节码文件加载到内存,并对数据进行校验、转换解析和初始化,最终形成可以被虚拟机直接使用的Class对象,这就是虚拟机的类加载机制。
类从被加载到虚拟机内存中开始,到卸载出内存为止,它的整个生命周期包括:加载、验证、准备、解析、初始化、使用和卸载7个阶段。前面5个阶段是类加载的过程,初始化阶段是类加载过程的最后一步,在初始化阶段,会为类的静态变量赋予初始值(声明静态变量时显式赋值,或者在static块中赋值),要注意的是,在准备阶段,虚拟机已经为类的静态变量初始化默认值,而在初始化阶段的赋值是我们程序主观给定的初始值,两者并不冲突。到了初始化阶段,才真正开始执行类中定义的程序代码。
相信很多程序猿都有过这样一个疑问,什么情况下需要开始类加载过程中的第一阶段:加载?(这里加载指的是将类的.class文件中的二进制数据读入到内存中,将其放入到运行时数据区的方法区内,然后在堆区创建一个java.lang.Class对象,用来封装类在方法区内的数据结构)我同样也被这个问题困扰过,Java虚拟机规范中对何时开始加载并没有进行强制约束,交给虚拟机的具体实现来自由把握。Java语言在运行期间是动态加载的,在第一次需要使用类信息时会进行加载。比如:调用静态成员变量或方法、实例化一个对象、加载子类时会先加载父类。
但是对于初始化阶段,虚拟机规范则严格的规定了有且只有在对类首次主动使用时,才执行初始化操作(加载、验证、准备阶段在此之前进行)。
Java程序对类的使用方式分为两种:主动使用和被动使用。所有的Java虚拟机实现必须在每个类或接口被Java程序“首次主动使用”时才初始化这个类,否则不执行初始化阶段操作。
什么是对类的主动使用和被动使用?在Java程序中,一共有6种情况会被称为对类的主动使用,除了这6种,其他所有的对类的使用方式都被称为被动使用。当这6种方式出现时如果类没有进行过初始化,则会触发其进行初始化。
主动使用(6种):
1、创建类的实例
2、访问某个类或接口的静态变量,或者对该静态变量赋值
3、调用某个类的静态方法
4、反射(Class.forName("java.lang.String"))
5、初始化一个类的子类
6、Java虚拟机启动时被标明为启动类的类
前3种情况很好理解,第4种运用反射返回一个Class对象,这时与Class对象相关联的类会被初始化。第5种初始化一个类的子类是指,给子类的静态变量赋予初始值,这时父类会先进行初始化。第6种当一个类被标明为启动类,例如一个类中含有main方法,这个类就是启动类,Java虚拟机启动时会加载并初始化这个类。
这里再强调一遍,除了以上6种情况,其他所有对类的操作都不会触发虚拟机对类进行初始化操作,即在类加载过程中不会执行初始化这一阶段。