10.2 Java类的初始化

当一个Java类第一次被真正使用时,虚拟机会对该Java类进行初始化。初始化的主要工作是执行Java类中的静态代码块和初始化静态域。在初始化过程中,Java类中的静态代码块和静态域会按照在代码中出现的顺序依次执行初始化。在当前Java类被初始化之前,它的直接父类也会被初始化,但是该Java类所实现的接口并不会被初始化。对一个接口来说,当其被初始化时,它的父接口不会被初始化。

在初始化的过程中,静态代码块和静态域的出现顺序很重要。虚拟机会严格按照在源代码中的出现顺序来执行初始化操作。代码清单10-2展示了静态域的初始化顺序可能带来的问题。静态域X的值最初是20,所以Y的值是40。虽然随后通过静态代码块把X的值设成了30,但是Y的值不会发生变化,仍然是40。

代码清单10-2 静态域的初始化顺序的示例


public class StaticOrder{

public static int X=20;

public static int Y=2*X;

static{

X=30;

}

public static void main(String[]args){

System.out.println(Y);//输出40

}

}


需要注意的是,当访问一个Java类或接口中的静态域时,只有真正声明这个域的类或接口才会被初始化。代码清单10-3给出了一个类继承时的静态域的初始化示例。在类A中声明了静态域value,类B继承自类A。通过B.value可以直接访问类A中声明的静态域value。虽然引用时使用的是类B,但是由于value是在类A中声明的,因此访问B.value只会使类A被初始化,类B不会被初始化。

代码清单10-3 类继承时的静态域的初始化示例


class A{

static int value=100;

static{

System.out.println("类A初始化。");

}

}

class B extends A{

static{

System.out.println("类B初始化。");

}

}

public class StaticFieldInit{

public static void main(String[]args){

System.out.println(B.value);

}

}


有很多不同的原因会使一个Java类被初始化。下面列出了可能造成类被初始化的操作。

1)创建一个Java类的实例对象。比如调用“MyClass obj=new MyClass()”语句时,类MyClass会被初始化。

2)调用一个Java类中的静态方法。比如myMethod是MyClass类中的静态方法,调用myMethod方法会使MyClass类被初始化。

3)为类或接口中的静态域赋值。比如myField是MyClass类中的静态域,调用“MyClass.myField=10”会使MyClass类被初始化。

4)访问类或接口中声明的静态域,并且该域的值不是常值变量。常值变量是声明为final的Java基本类型或String类型的变量,使用编译时常量来初始化。比如,代码“private static final String name="Alex";”中的域name的值是常值变量。如果类MyClass中的静态域myField的值不是常值变量,访问myField域的操作会使类MyClass被初始化。

5)在一个顶层Java类中执行assert语句也会使该Java类被初始化。

6)调用Class类和反射API中进行反射操作的方法也会初始化Java类。