public class StaticTest { public static int k = 0; public static StaticTest t1 = new StaticTest("t1"); public static StaticTest t2 = new StaticTest("t2"); public static int i = print("i"); public static int n = 99; public int j = print("j"); { print("构造块"); } static { print("静态块"); } public StaticTest(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); ++i; return ++n; } public static void main(String[] args) { StaticTest t = new StaticTest("init"); } }
1. 首先加载类,加载类过程中对于定义的静态字段分配内存空间,也是在方法区中,并进行零初始化,即数字类型初始化为0,boolean初始化为false,引用类型初始化为null等。这也就是为什么刚开始i=0,n=0
2. 执行第一句public static int k = 0;对k进行赋值
3. 执行第二句public static StaticTest t1 = new StaticTest("t1");这是定义了一个类的静态对象,在t1对象初始化时先执行非静态方法或者非静态常量,顺序执行,接着运行构造参数。意思是当声明了对象名后,我的类有了一个对象,就默认调用了类中定义的非静态的变量 和方法,依次进行;所以这时就会调用public int j = print("j");
此时会调用print方法,其中++i,i=1=i+1; 返回值++n,n=1=n+1;j=1,k=1.
打印 1:j i=0 n=0;
然后执行非静态块print("构造块");调用print函数
打印 2:构造块 i=1 n=1
没有了非静态的方法,块或常量后,之后会对t1初始化,执行类的构造函数public StaticTest(String str);
打印 3:t1 i=2 n=2
4. 执行第三句public static StaticTest t2 = new StaticTest("t2");和上面一步的分析方法一样。
依次打印
4:j i=3 n=3
5:构造块 i=4 n=4
6:t2 i=5 n=5
5. 执行第四句public static int i = print("i");调用print方法
打印 7:i i=6 n=6
6. 执行第五句public static int n = 99;至此静态变量加载完毕,然后加载静态块和静态方法的声明,只有当调用静态方法时才执行静态方法的内容。
7. 执行第六句static{ print("静态块"); } 所以调用print函数
打印 8:静态块 i=7 n=99 由于n在上一步初始化了。
8. 执行main函数体StaticTest t = new StaticTest("init");同样分析思路和第3步是一样的,当声明了对象t时,程序会先执行非静态块和变量以及非静态方法的声明。然后执行类对象的初始化。所以先执行public int j = print("j");调用了print函数
打印 9:j i=8 n=100
然后执行非静态块print("构造块");调用了print函数
打印 10:构造块 i=9 n=101
最后执行对象的初始化,调用了构造函数public StaticTest(String str);
打印 11:init i=10 n=102
程序的入口函数依然是main函数,这里的知识点在于程序时类的加载过程和java虚拟机的内存分配。程序执行的顺序是,java虚拟机加载由.java编译生成出的.class字节码文件,将其加载至内存,然后找到了main函数,但并不是运行main函数的第一句,而是加载类的数据,当加载一个类时,JVM会根据属性的数据类型第一时间赋默认值(一举生成的),即数字类型初始化为0,boolean初始化为false,引用类型初始化为null等。然后再进行静态属性初始化,并为静态属性分配内存空间,静态方法的声明,静态块的加载,没有优先级之分,按出现顺序执行,静态部分仅仅加载一次。至此为止,必要的类都已经加载完毕,对象就可以被创建了。
当对象被创建时,就会调用类的动态属性,普通方法声明,构造块。最后在调用构造函数。
总的来说调用过程如下:
先加载类的静态属性(变量),静态块,静态方法的声明。
然后当定义了类的对象时,调用非静态属性(变量),构造块,普通方法声明。最后对象初始化调用类的构造函数。