虚拟机规范严格规定了有且只有5种情况必须立即对类进行初始化。
1)遇到new、getstatic、putstatic或invokestatic这4条字节码指令时,如果类没有进行过初始化,则需要先触发其初始化。
2)使用java.lang.reflect包的方法对类进行反射调用的时候,如果类没有进行过初始化,则需要先触发其初始化。
3)当初始化一个类的时候,如果发现其父类还没有进行过初始化,则需要先触发其父类初始化。
4)当虚拟机启动时,用户需要指定一个要执行的主类(包含main方法的),虚拟机会优先初始化这个主类。
5)当使用JDK1.7的动态语言支持时,如果一个java.lang.invoke.MethodHandle实例最后的解析结果REF_getStatic等时,这个方法的类还没有进行过初始化,则需要先触发其初始化。
在这里讲几个静态语句的例子,如:
public class ConstClass {
static
{
System.
out
.println (
"ConstClass init!!!"
);
}
public
static
final
String
HELLOWORLD
=
"hello world"
;
public
static
final
int
num
= 1;
}
public
class
Test {
public
static
void
main(String[] args) {
System.
out
.println(
""
+ ConstClass.
num
);
}
}
最后的执行结果为:1,并没有输出ConstClass init!!!。
类Test虽然引用了类ConstClass的常量,但类ConstClass不会因为类Test的引用而初始化,因为类Test已经把类ConstClass中的常量num给引入到自己的常量池中了,所以类Test对类ConstClass常量的引用其实是对类Test自身的常量引用。
类的常量是在类的Class文件中定义的,位于魔数、版本号之后。
如果所ConstClass改为:
public
class
ConstClass {
static
{
System.
out
.println (
"ConstClass init!!!"
);
}
public
static
final
String
HELLOWORLD
=
"hello world"
;
public
static
int
num
= 1;
}
1
所以能看出必须是常量,不能只是静态的。
还有一种情况是子类跟父类的引用关系,如:
public
class
SuperClass {
static
{
System.
out
.println (
"SuperClass init!!!"
);
}
public
static
int
num
= 123;
}
public
class
SubClass
extends
SuperClass{
static
{
System.
out
.println (
"SubClass init!!!"
);
}
}
public
static
void
main(String[] args) {
System.
out
.println(
""
+ SubClass.
num
);
}
123
这是因为对于静态字段,只有直接定义这个字段的类才会被初始化,因此通过其子类来引用父类中定义的静态字段,只会触发父类的初始化,而不会触发子类的初始化。