Java字节代码的表现形式是字节数组(byte[]),而Java类在JVM中的表现形式是java.lang.Class类的对象。一个Java类从字节代码到能够在JVM中被使用,需要经过加载、链接和初始化这三个步骤。这三个步骤中,对开发人员直接可见的是Java类的加载,通过使用Java类加载器(class loader)可以在运行时刻动态的加载一个Java类;而链接和初始化则是在使用Java类之前会发生的动作。本文会详细介绍Java类的加载、链接和初始化的过程。
类加载的初始化阶段对类变量赋予正确的值。主要有两种初始化方式,一种是通过类变量初始化语句;一种是静态初始化语句。如下述代码所示,前者是类变量初始化语句,后者是静态初始化语句。
public class Example1 {
static int width;
static int height = (int) (Math.random() * 2.0);
static {
width = (int) (3 * Math.random() * 5.0);
}
}
所有的类变量初始化语句和静态初始化语句都被Java编译器收集在一起,放在一个特殊方法里。对于类而言,该方法称为类初始化方法,对于接口而言,该方法称为接口初始化方法。在Java class文件里,类和接口的初始化方法统一被称作为() 方法。并且这种方法只能被Java虚拟机调用,Java程序是无法调用的。
Java类的初始化
当一个Java类第一次被真正使用到的时候,JVM会进行该类的初始化操作。初始化过程的主要操作是执行静态代码块和初始化静态域。在一个类被初始化之前,它的直接父类也需要被初始化。但是,一个接口的初始化,不会引起其父接口的初始化。在初始化的时候,会按照源代码中从上到下的顺序依次执行静态代码块和初始化静态域。
public class StaticTest {
public static int X = 10;
public static void main(String[] args) {
System.out.println(Y); //输出60
}
static {
X = 30;
}
public static int Y = X * 2;
}
在上面的代码中,在初始化的时候,静态域的初始化和静态代码块的执行会从上到下依次执行。因此变量X的值首先初始化成10,后来又被赋值成30;而变量Y的值则被初始化成60。
类加载以及初始化的顺序是:类的静态成员初始化分配内存(有父类的先父类,然后自己)->然后main方法执行->类初始化{类非静态成员的初始化,然后执行构造器(有父类的先给父类的非静态成员初始化,然后父类的构造器)}