加载阶段,java虚拟机规范中并没有进行强制约束;
但初始化阶段,虚拟机规范则严格规定了有且只有5种情况必须立即对类进行“初始化”;
1)遇到new,getstatic,putstatic 或invokestatic指令,如果类没有进行过初始化,则需要先触发初始化;
即使用new 实例化对象,读取或设置一个类的静态变量(被final修饰,已在编译期把结果放入常量池的静态字段除外)的时候,以及调用一个类的静态方法的时候。
2)使用java.lang.reflect 包的方法对类进行反射调用的时候,如果类没有进行过初始化,则需要先触发其初始化;
3)当初始化一个类的时候,如果发现父类还没有进行过初始化,则需要先触发其父类的初始化。
4)当虚拟机启动时,用户需要指定一个要执行的主类(包括 main()方法的那个类),虚拟机会先初始化这个类;
5)当使用JDK1.7 的动态语言支持时,如果一个java.lang.invoke.MethodHandle 实例最后的解析结果REF_getStatic、REF_putStatic、REF_invokeStatic的方法句柄,并且这个句柄所对应的类没有进行初始化,则需要先触发其初始化。
举几个例子说明:
public class Parent {
public static String PARENT ="parent";
public static final String FINAL_PARENT = "final_parent";
static {
System.out.println("parent static code");
}
public Parent(){
System.out.println("parent init");
}
public static String getStatic(){
return "parent static method";
}
}
public class Sub extends Parent {
public static String PARENT ="sub";
static {
System.out.println("sub static code");
}
public Sub(){
System.out.println("sub init");
}
// public static String getStatic(){
// return "sub static method";
// }
}
例子1: 没有new,不会初始化Parent 类
public class Main {
public static void main(String[] args){
Parent parent;
}
}
结果:没有任何输出
查看Main.class,可以看出Main这个类没有Parent类的入口,说明编译的时候就会遵守虚拟机的一些规则
public class Main {
public Main() {
}
public static void main(String[] args) {
}
}
例子2: new 会引发类初始化
public class Main {
public static void main(String[] args){
Parent parent =
new Parent();
}
}
结果:输出:
parent static code
parent init
查看Main.clas,有了new Parent入口
public class Main {
public Main() {
}
public static void main(String[] args) {
new Parent();
}
}
例子3: 获取static变量,会类初始化,不会调用构造函数
public class Main {
public static void main(String[] args){
System.
out.println(Parent.
PARENT);
}
}
输出:
parent static code
parent
对应class类
public class Main {
public Main() {
}
public static void main(String[] args) {
System.out.println(Parent.PARENT);
}
}
例子4:设置static变量,会类初始化,不会调用构造函数
public class Main {
public static void main(String[] args){
Parent.
PARENT =
"hello";
}
}
输出:
parent static code
例子5: 调用被final修饰,已在编译期把结果放入常量池的静态字段,不会类初始化
public class Main {
public static void main(String[] args) {
System.
out.println(Parent.
FINAL_PARENT);
}
}
只输出:final_parent
对应class为:
public class Main {
public Main() {
}
public static void main(String[] args) {
System.out.println(
"final_parent");
}
}
例子6: 调用类的静态方法,类会初始化
public class Main {
public static void main(String[] args) {
System.
out.println(Parent.
getStatic());
}
}
输出:
parent static code
parent static method
例子7: 通过子类调用父类的static方法,不会初始化子类
public class Main {
public static void main(String[] args) {
System.
out.println(Sub.
getStatic());
}
}
输出:
parent static code
parent static method
如果将Sub类中的注释部分解开;
输出:
parent static code
sub static code
sub static method
例子8: new子类,会先初始化其父类
public class Main {
public static void main(String[] args) {
new Sub();
}
}
输出:
parent static code
sub static code
parent init
sub init