看过Thinking in java的人都知道,在不考虑父类的情况下,类的初始化顺序相对简单,总体是先静态的,然后是非静态的。这里都包括成员变量和代码块。对于静态变量和静态代码块,不管它们在何处定义,总是按照其先后顺序进行初始化。同理,非静态变量和非静态代码块也是如此。
下面上热菜:
public class Test1 {
public static int k = 0;
public static Test1 initOrder1 = new Test1("t1");
public static Test1 initOrder2 = new Test1("t2");
public static final int m = -1;
public static int i = print("i");
public static int n = 99;
public int j = print("j");
{
print("构造块");
}
static {
print("静态块");
}
public Test1(String str) {
System.out.println((++k) + ":" + str + " i=" + i + " n=" + n + " m=" + m);
++i;
++n;
}
public static int print(String str) {
System.out.println((++k) + ":" + str + " i=" + i + " n=" + n + " m=" + m);
++n;
return ++i;
}
public static void main(String[] args) {
Test1 initOrder = new Test1("init");
}
}
什么!!!居然还有这样的的:
public static Test1 initOrder1 = new Test1("t1");
对我这种小白来说,这太不能理解了。伤脑细胞!
我们先看看答案吧:
1:j i=0 n=0 m=-1
2:构造块 i=1 n=1 m=-1
3:t1 i=2 n=2 m=-1
4:j i=3 n=3 m=-1
5:构造块 i=4 n=4 m=-1
6:t2 i=5 n=5 m=-1
7:i i=6 n=6 m=-1
8:静态块 i=7 n=99 m=-1
9:j i=8 n=100 m=-1
10:构造块 i=9 n=101 m=-1
11:init i=10 n=102 m=-1
按照惯例,程序进入main()方法后,在调用new Test1()的一瞬间,Test1开始初始化了,k=0。当进行到public static Test1 initOrder1 = new Test1("t1");的时候,就要开始实例化了,所以接下来的静态变量都先等着,等排在前面的initOrder1先办完事了,才有你们这群小屁孩的事。因为静态变量属于类,而非类的实例,所以我initOrder1只好先对实例变量和非静态代码块下手了(小鲜肉,我来了。。。)。所以就会执行如下语句:
public int j = print("j");
{
print("构造块");
}
得到输出:
1:j i=0 n=0 m=-1
2:构造块 i=1 n=1 m=-11
然后再调用构造器,就可以完成对initOrder1的初始化,顺便打印出了
3:t1 i=2 n=2 m=-1
接下来该初始化initOrder2了,原理同上。打印出:
4:j i=3 n=3 m=-1
5:构造块 i=4 n=4 m=-1
6:t2 i=5 n=5 m=-1
然后继续其他成员的初始化。注意,当初始化到了
public static int n = 99;
就会初始化静态代码块哟,要注意哈。别以为是顺序下来的。当初始化全部完成后,然后调用Test1("init"),至此,完成了mian()方法中的变量初始化。大功告成。
在虚拟机进行类加载的过程中,有一个准备阶段。准备阶段是正式为类变量分配内存并设置初始值的阶段,而且变量所使用的内存都将在方法区中进行分配。强调一下,此时金分配内存的是类变量(即被static修饰的变量),不包括实例变量,实例变量会和对象实例化的时候随着对象一起分配到java堆中。而且,在准备阶段过后,类变量如k和n的值为0, 而非通过=符号来赋值0和99。但是对于final static的变量m来说,情况就有点不一样。m在准备阶段就会被赋予初值-1,并放入常量池中。
如果有什么说错或者不清楚的地方,欢迎各位看官不吝批评指正。