类的初始化详解
在深入理解JVM之类加载器(一)里面我主要介绍了JVM中关于类的加载相关的一些知识,那在本文我将详细介绍类的初始过程。我先编写三个类,代码如下:
public class MyTest {
public static void main(String[] args) {
System.out.printf(MyChild1.str);
// System.out.printf(MyChild1.str2);
}
}
class MyParent{
public static String str="hello world";
static {
System.out.println("this is MyParent static");
}
}
class MyChild extends MyParent{
public static String str2="welcome!!";
static {
System.out.println("this is MyChild static");
}
}
三个类的类名分别为MyTest,MyParent , MyChild。其中MyChild是MyParent的子类,而MyTest拥有main方法,是一个启动类。
首先我们在MyTest这个类中运行一下如下代码:
System.out.printf(MyChild.str);
输出结果为:
this is MyParent static
hello world
接着把刚才的代码改为如下代码再运行:
System.out.printf(MyChild.str2)
输出结果为:
this is MyParent static
this is MyChild static
welcome!!
结果分析:
第一次我们通过MyChild这个类调用它父类MyParent中的str这个静态变量,输出结果表明在执行 MyChild.str 时,执行了MyParent这个类中定义的静态代码块,但是没有执行MyChild中的静态代码块。而在第二次运行时我们将str换成了MyChild这个类中定义的静态变量str2,从输出结果看出,此时代码在运行时先执行了MyParent这个类中的静态代码块,接着执行了MyChild中的静态代码块,最后才输出了str2的值。从以上分析我们得出结论:
- 对于静态字段来说,调用时只有直接定义了该字段的类才会被初始化:在第一次运行时,我们访问了MyParent中的静态变量,即我们对MyParent这个类进行了主动使用,但是没有访问MyChild中的静态变量,所以并没有主动使用MyChild这个类,因此MyChild中的静态代码块不会执行。总之,谁定义了静态变量,使用这个静态变量时就表示对谁的主动使用。
- 当一个类在初始化时,要求其父类全部初始化完毕后,该类才会被初始化:在第二次运行时,我们访问了MyChild中的静态变量,则主动使用了MyChild这个类,所以MyChild中的静态代码块被执行了,但是却先执行了MyParent中的静态代码块,这是因为MyChild是MyParent的子类,当初始化一个类的子类时父类也会主动使用。这一点我们可以通过配置虚拟机参数 -XX:+TraceClassLoding(用于追踪类的加载信息并按顺序打印出来)观察得出,idea中配置方法如图:
具体加载信息由于篇幅太长我就不贴出来了,可自行配置并运行程序观察。