public class T implements Cloneable { public static int k = 0; public static T t1 = new T("t1"); public static T t2 = new T("t2"); public static int i = print("i"); public static int n = 99; public int j = print("j"); { print("构造块"); } static { print("静态块"); } public T(String str) { System.out.println((++k) + ":" + str + " i=" + i + " n=" + n); ++n; ++i; } public static int print(String str) { System.out.println((++k) + ":" + str + " i=" + i + " n=" + n); ++n; return ++i; } public static void main(String[] args) { } }
输出:
1:j i=0 n=0
2:构造块 i=1 n=1
3:t1 i=2 n=2
4:j i=3 n=3
5:构造块 i=4 n=4
6:t2 i=5 n=5
7:i i=6 n=6
8:静态块 i=7 n=99
首先从main函数进入程序,类中的静态成员会被先加载,加载的时候,JVM首先将类中静态属性的声明和静态方法的声明加载到内存中(方法区中),然后将静态属性赋值,之后执行静态块中的代码。如果在类加载的时候遇到实例化的情况,首先加载普通属性的声明,然后普通方法的声明,之后是普通属性赋值,执行构造块代码,最后执行构造函数中的代码。如果有继承关系,父类优先于子类。这是基本的加载顺序。
接下来我们逐行来分析。首先加载类中的静态属性声明,(static )int k, T t1, T t2, int i, int n, (只有声明还没有赋值),然后加载静态方法声明static int print(String str) {}。之后对静态属性赋值,首先加载k = 1; 当加载到t1 = new T("t1")时,这是实例化的过程,这时类暂时不加载。按照上面所说的,实例化的时候首先加载类中的普通成员声明 int j, 然后是普通方法声明,public T(String str){},之后对普通属性赋值,这时j = print("j"), 就调用了public static int print(String str){} 方法,这时的i和n还没有被赋值,仅仅是JVM在声明时给它分配的初始值,都为0。因此这时的输出为1:j i=0 n=0。这是普通属性的赋值,接下来是构造块的执行因此有了第二行输出:2:构造块 i=1 n=1,最后是构造函数执行,有了第三行输出:3:t1 i=2 n=2。到现在t1的实例化过程结束,接着进行类的加载。 接下来对t2进行复制,也是一个实例化的过程,和上面蓝色字体描述的一样, 于是就有了4,5,6三行输出。再下面对static int i进行复制 i = print("i") 因此有了第七行输出。接下来对public static n 进行赋值, n = 99。此时静态属性赋值全部结束,最后加载静态块中的内容,print("静态块"), 因为这时的n已经被赋值了,因此最后的输出为:8:静态块 i=7 n=99