JVM初始化一个类包含如下步骤:
- 假如这个类还没有被加载和连接,程序先加载并连接该类
- 假如该类的直接父类还没有初始化,则先初始化其直接父类
- 假如类中有初始化语句,则系统依次执行这些初始化语句。
类初始化的时机:
- 创建类的实例:new,反射等。
- 调用某个类的静态方法
- 访问某个类或接口的静态属性
- 使用反射方式来强制创建某个类或接口对应的Class的对象。
- 初始化某个类的子类,该子类的所有父类都会被初始化。
- 直接使用java命令来运行某个类,当运行某个主类时,程序会先初始化该主类。
特殊情况:
- 对于一个final型的静态属性,如果该属性可以在编译时就得到属性值,则可认为该属性可被当成编译时常量。当程序使用编译时常量时,系统会认为这是对该类的被动使用,所以不会导致该类的初始化。
- 如果final类型的静态属性的值不能在编译时得到,必须等到运行时才可以确定,如果通过该类来访问该静态属性,则可以认为是主动访问使用该类,将会导致该类被初始化。
- 当使用ClassLoader类的loadClass方法来加载某个类时,该方法只是加载该类,并不会执行该类的初始化。当使用Class的forName静态方法才会导致强制初始化该类。
- 只有当程序访问的静态变量或静态方法确实在当前类或当前接口中定义时,才可以认为是对类或接口的主动使用
特殊情况1
class FinalTest1 {
public static final int a = 3;
static {
System.out.println("FinalTest1 is initialization");
}
}
public class Test1 {
public static void main(String[] args) {
System.out.println(FinalTest1.a);
}
}
输出结果
3
解析:当Test1
执行main方法时,FinalTest1
中的a
在编译时就得到属性值,故不会对FinalTest1
进行初始化操作,所以不会出现FinalTest1 is initialization
特殊情况2
class FinalTest2 {
public static final int a = new Random().nextInt(10);
static {
System.out.println("FinalTest2 is initialization");
}
}
public class Test2 {
public static void main(String[] args) {
System.out.println(FinalTest2.a);
}
}
输出结果
FinalTest2 is initialization
5
解析:当Test2
执行main方法时,FinalTest2
中的a
不能在编译时得到,必须等到运行时才可以确定,故会对FinalTest2
进行初始化操作,所以会出现FinalTest2 is initialization
特殊情况3
class FinalTest3 {
static {
System.out.println("FinalTest3 is initialization");
}
}
public class Test3 {
public static void main(String[] args) throws ClassNotFoundException {
ClassLoader classLoader = ClassLoader.getSystemClassLoader();
Class<?> clazz = classLoader.loadClass("com.api.program.activity.controller.FinalTest3");
System.out.println("-----------------");
clazz = Class.forName("com.api.program.activity.controller.FinalTest3");
}
}
输出
-----------------
FinalTest3 is initialization
解析:当Test3
执行main方法时,首先会执行ClassLoader
加载类,然后再用class反射加载。故只有反射
特殊情况4
class Parent{
public static final int a=new Random().nextInt(10);
static {
System.out.println("parent is initialization");
}
}
class Son extends Parent{
static {
System.out.println("son is initialization");
}
}
public class Test4 {
public static void main(String[] args) {
System.out.println(Son.a);
}
}
输出
parent is initialization
6
解析:因为变量a
未在Son
,所以执行main方法不会初始化Son
。