实验过程

结论一

静态代码块并不一定初始化在静态成员前,两者顺序关系与代码的顺序有关,依据如下实验

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
/**
* 测试类的初始化顺序
* @author 神奇女侠
*
*/

public class MyTest {


//静态成员
public static MyTest t=new MyTest();

//静态代码块
static
{
System.out.println("这里是MyTest类的静态代码块");
}

//构造函数
public MyTest()
{
System.out.println("构造函数被执行");
}

//主函数
public static void main(String[] args)
{

}

}

执行结果:

构造函数被执行
这里是MyTest类的静态代码块

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
/**
* 测试类的初始化顺序
* @author 神奇女侠
*
*/

public class MyTest {
//静态代码块
static
{
System.out.println("这里是MyTest类的静态代码块");
}

//静态成员
public static MyTest t=new MyTest();

//构造函数
public MyTest()
{
System.out.println("构造函数被执行");
}

//主函数
public static void main(String[] args)
{

}
}

执行结果:

这里是MyTest类的静态代码块
构造函数被执行

结论二

内部类的静态代码块与静态成员的初始化顺序也与代码中的顺序有关

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
public class MyTest {
//静态代码块
static
{
System.out.println("这里是MyTest类的静态代码块");
}

//静态成员
//public static MyTest t=new MyTest();

//构造函数
public MyTest()
{
System.out.println("构造函数被执行");
}

//内部类
public static class Inner
{
//内部类的静态代码块
static
{
System.out.println("这里是Inner类的静态代码块");
}

//静态成员
public static MyTest t=new MyTest();

public static void Method()
{
System.out.println("Method()被执行");
}
}

//主函数
public static void main(String[] args)
{
MyTest.Inner.Method();
}
}

执行结果:

这里是MyTest类的静态代码块
这里是Inner类的静态代码块
构造函数被执行
Method()被执行

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
public class MyTest {
//静态代码块
static
{
System.out.println("这里是MyTest类的静态代码块");
}

//静态成员
//public static MyTest t=new MyTest();

//构造函数
public MyTest()
{
System.out.println("构造函数被执行");
}

//内部类
public static class Inner
{

//静态成员
public static MyTest t=new MyTest();

//内部类的静态代码块
static
{
System.out.println("这里是Inner类的静态代码块");
}


public static void Method()
{
System.out.println("Method()被执行");
}
}

//主函数
public static void main(String[] args)
{
MyTest.Inner.Method();
}
}

这里是MyTest类的静态代码块
构造函数被执行
这里是Inner类的静态代码块
Method()被执行

结论三

类的使用需经过加载(将二进制文件,即.class文件加载进内存),链接,初始化。静态代码块的执行与静态成员的初始化都在类的初始化时期;也就是说,类只有被使用时才会完成初始化。
分析:MyTest的静态代码块始终首先被执行,是因为jvm使用了MyTest内的main方法。所以MyTest在执行初的时候就完成了加载,链接,初始化三步。

而Inner可以理解为是MyTest的静态成员,应该在一开始就完成了初始化,但是Inner内的静态代码块却没有在MyTest的静态成员初始化的时候执行,说明这时的Inner只是完成了加载,链接两步。直到调用了Inner内的Method方法,Inner的初始化才被完成。

结论四

只有类完成初始化之后(加载,链接,初始化),才可以new对象。new对象的初始化顺序为:构造代码块,成员变量初始化,构造函数(先父后子)

总结:类的初始化与对象的初始化是不一样的,也就可以理解为什么静态的只会执行一次,而非静态new多少次就执行多少次。因为静态的初始化是在类本身的初始化时期进行的,与对象的初始化不同。

总结

引用一下别人的总结:

顺序是:父类静态属性->父类静态代码块->子类静态变量->子类静态代码块->父类非静态变量->父类非静态代码块->父类构造函数->子类非静态变量->子类非静态代码块->子类构造函数
这样的加载顺序不是绝对的 因为静态变量和静态代码块跟声明顺序有关。
对于如果静态代码块中调用静态变量,那么静态变量必须在静态代码块前面声明;如果静态代码块中没有调用静态变量,那么就跟顺序有关了,谁先声明谁先被加载。说白了还是顺序加载,之所以会出现“如果静态代码块中调用静态变量,那么静态变量必须在静态代码块前面声明”,是因为变量是声明,所以出现编译错误。
应用到内部类中 静态变量和静态代码块跟声明顺序有关。 这样就可以解释你的问题了。内部类也是类。
测试所用jdk版本1.8.0_20
类静态块-类静态属性这个跟顺序有关系 如果类静态属性在类静态代码块之前 那么类静态属性先初始化

Reference:

https://www.oschina.net/question/2273217_217864
http://www.cnblogs.com/yahokuma/p/3668138.html

如有错误,请多指教