加载->验证->准备->解析->初始化java
或者面试
加载->验证->准备->初始化->解析数组
什么状况下须要开始类加载过程的第一个阶段:加载?Java虚拟机规范中并无进行强制约束,这点能够交给虚拟机的具体实现来自由把握。可是对于初始化阶段,虚拟机规范则是严格规定了有且只有5种状况必须当即对类进行初始化(而加载、验证、准备天然须要在此以前开始): 有且仅有如下五种状况:安全
这段文字完彻底全来自于《深刻理解 Java 虚拟机》,我想强调的有两点:bash
一、以上五点是指类被初始化(参考初始化阶段)的时机,而不是被加载的时机。数据结构
二、加载、验证、准备这三个阶段能够先进行,而初始化能够迟迟不进行直到必要的时候编辑器
比较容易理解,其实就是将 .class 文件加载到内存,并生成一个 Class 对象来方便管理,具体流程以下:布局
简化流程图以下所示: spa
String[] strArr = new String[10];
复制代码
这个语句会涉及到两个类:翻译
验证是链接阶段的第一步,这一阶段的目的是为了确保Class文件的字节流中包含的信息符合当前虚拟机的要求,而且不会危害虚拟机自身的安全。验证大体上会完成下面四个阶段的检验工做:
准备阶段也比较简单,搞清楚如下几点便可:
public static int value=123;
只是将 value 赋值为 0 而不是 123。注:各类Java 各类基本类型 0 值对照表以下
public static final int value=123;
则 value 在准备阶段即被赋值为 123,至于缘由也是显而易见的
解析是将符号引用转化成直接引用的阶段。符号引用和直接引用在《深刻理解 Java 虚拟机》中的定义以下:
上面的定义看的一脸懵逼,我举个栗子来理解这段文字。
public class Test {
public static final mian(String[] args) {
System.out.println("Hello, /word!"); //1
}
}
复制代码
如上能够看到 Test 是一个很是简单的类,Test 类中调用了 System.out.pintln 接口向屏幕上输出 Hello, world 。该类的 .class 文件中的字符串常量中(层层转换后)确定会包含如下的一个常量:
invokevirtual java/io/PrintStream.println:(Ljava/lang/String;)V
复制代码
很明显能够看出以上是一个方法的调用(即 System.out.println())指令:其中 java/io/PrintStream 是方法所在的类,println 是方法名,Ljava/lang/String 表示参数类型,这样就能惟一肯定须要调用的方法,而java/io/PrintStream.println:(Ljava/lang/String;)V
即咱们的所说的符号引用。一样若是访问其余类、接口、类的字段、类的方法、接口的方法等,都须要在 .class 文件中经过符号引用指定。因为只是一个字符串,因此是虚拟机无关的。
Test 类的.class 文件最终是要被加载到虚拟机内存中才能被执行的,而在内存中方法的访问是经过地址实现的。因此须要将符号引用java/io/PrintStream.println:(Ljava/lang/String;)V
转换成一个指向方法在内存中具体地址(该方法所在的类一定完成了加载、验证、准备和解析的阶段)的指针,.class 中指令变成相似以下形式:
invokevirtual 0xfe1886d2;(配图以下)
复制代码
经过这个例子能够简单的理解符号引用和直接引用以及它们的关系,从而明白解析阶段的做用。
类初始化阶段是类加载过程的最后一步,该阶段才真正开始执行类中定义的Java程序代码(或者说是字节码)。
类初始化过程即执行 <clinit>()方法的过程,而 <clinit>() 方法又是由编译器自动收集类中的全部类变量的赋值动做和静态语句块(static{}块)中的语句合并产生的,编译器收集的顺序是由语句在源文件中出现的顺序所决定的。须要注意的是:
静态语句块中只能访问到定义在静态语句块以前的变量,定义在它以后的变量,在前面的静态语句块能够赋值,可是不能访问。
初始化的时机在类加载加载时机一章节已经列出,是为了区分类加载时机和类初始化时机。只有清晰的理解须要初始化的各类场景才能真正把握初始化的含义。
类的加载不少人似懂非懂,特别是解析和初始化:准备和初始化阶段完成了 static 变量的内存分配和赋值以及 static 代码块中代码的执行。出道网上的面试题,在不百度的状况下写出最后输出的字符串,若是能正确写出答案那大几率已经弄懂了解析和初始化阶段,面试题以下:
class Grandpa {
static
{
System.out.println("爷爷在静态代码块");
}
}
class Father extends Grandpa {
static
{
System.out.println("爸爸在静态代码块");
}
public static int factor = 25;
public Father() {
System.out.println("我是爸爸~");
}
}
class Son extends Father {
static
{
System.out.println("儿子在静态代码块");
}
public Son() {
System.out.println("我是儿子~");
}
}
public class InitializationDemo {
public static void main(String[] args) {
System.out.println("爸爸的岁数:" + Son.factor); //入口
}
}
复制代码