Java类加载过程
为什么说Java语言是“一处编译,到处运行”?
首先我们编写的是Java文件,经过Java编译器处理之后我们会得到一个字节码也就是.class文件,生成的这个.class文件就是可以到处运行的文件。
那么这个class文件是怎么加载到内存中的呢?先来看一张宏观图
从上图可以看出class文件是通过“类装载器”把文件加载到内存中的。其实可以一句话来解释:类的加载指的是将类的.class文件中的二进制数据读入到内存中,将其放在运行时数据区的方法区内,然后在堆区创建一个 java.lang.Class对象,用来封装类在方法区内的数据结构。
那么在类加载过程中都做了哪些事?
什么时候才会进行类的加载?类的加载方式分为两大类
(1)隐式加载
创建类对象
使用类的静态域
创建子类对象
使用子类的静态域
在JVM启动时,BootStrapLoader会加载一些JVM自身运行所需的class
在JVM启动时,ExtClassLoader会加载指定目录下一些特殊的class
在JVM启动时,AppClassLoader会加载classpath路径下的class,以及main函数所在的类的class文件
(2)显式加载
ClassLoader.loadClass(className),只加载和连接、不会进行初始化。
Class.forName(String name, boolean initialize,ClassLoader loader); 使用loader进行加载和连接,根据参数initialize决定是否初始化。
加载.class文件的方式:
把一个java源文件动态编译成class文件
或者从本地文件系统内加载class文件,
从jar包加载class文件,
通过网络加载class文件。
总结:
类加载后,在准备阶段就已经对类中所有变量进行了内存的分配,并赋予了初始值,之后进入到初始化阶段,优先进行静态变量的初始化(加载顺序按照代码的顺序依次执行),然后进行类变量的初始化(加载顺序按照代码的顺序依次执行)。
如果有父类的话,则先进行父类的加载,同上
举个例子
按照上述总结,从main方法入口开始执行
第一步:进行静态变量、代码块的初始化,从上到下依次执行,发现只有一个静态变量i 赋值为1
第二步:进行main方法中的代码第一行 输出1
第三步:main方法中第二行代码创建类对象,进入有参构造方法,在执行方法体内容之前。要先去执行类变量、代码块代码,从上到下依次执行,a赋值为0,代码块第一行执行得到0,第二行执行得到“打印第1次”,执行完成
第四步:进行构造方法体内的代码,得到“有参构造”。
注意要点:
静态语句块中只能访问到定义在静态语句块之前的变量,而定义在它之后的变量,在前面的静态语句块可以赋值,但不能访问,代码解释如下: