问题重现
让我们先来看一下以下的程序:
2 // -------------------Static fields-------------------
3 private static int staticIntVar = 10 ;
4 private static int staticComputeIntVar = ( int )(Math.random() * 10 );
5 private static String staticStrVar = " Static field init(before) " ;
6 private static Object staticRefVar = new Object();
7
8 static {
9 staticIntVar = 20 ;
10 staticStrVar = " Static block init(before) " ;
11 staticAfterIntVar = 40 ;
12 staticAfterStrVar = " Static block init(after) " ;
13 }
14
15 private static int staticAfterIntVar = 30 ;
16 private static String staticAfterStrVar = " Static field init(after) " ;
17
18 // ---------------------Instance fields----------------
19 private int fieldIntVar = 100 ;
20 private int fieldComputeIntVar = ( int )(Math.random() * 100 );
21 private String fieldStrVar = " Instance field init(before) " ;
22
23 public StaticInitSequence() {
24 fieldIntVar = 200 ;
25 fieldStrVar = " Constructor field init(before) " ;
26
27 fieldAfterIntVar = 400 ;
28 fieldAfterStrVar = " Constructor field init(after) " ;
29 }
30
31 private int fieldAfterIntVar = 300 ;
32 private String fieldAfterStrVar = " Instance field init(after) " ;
33
34 public void print() {
35 System.out.println( " ----------------Static Fields------------ " );
36 System.out.println( " staticIntVar: " + staticIntVar);
37 System.out.println( " staticComputeIntVar: " + staticComputeIntVar);
38 System.out.println( " staticStrVar: " + staticStrVar);
39 System.out.println( " staticRefVar: " + staticRefVar);
40 System.out.println( " staticAfterIntVar: " + staticAfterIntVar);
41 System.out.println( " staticAfterStrVar: " + staticAfterStrVar);
42
43 System.out.println( " -----------------Instance Fields--------- " );
44 System.out.println( " fieldIntVar : " + fieldIntVar);
45 System.out.println( " fieldComputeIntVar : " + fieldComputeIntVar);
46 System.out.println( " fieldStrVar : " + fieldStrVar);
47 System.out.println( " fieldAfterIntVar : " + fieldAfterIntVar);
48 System.out.println( " fieldAfterStrVar : " + fieldAfterStrVar);
49 }
50 }
如果我们调用以上类的 print() 方法( new StaticInitSequence().print() ),会有什么样的结果呢?
我自认为,直接对一个字段初始化是编译器提供支持的一种编程方式,这种编程方式可以提高代码的可读性,因为用户可以直接知道一个字段的初始值,而不用到构造函数或者静态语句块里面去找。在 Java 中,实际编译后的二进制文件中,所有的字段初始化语句都放在了初始化函数中(类(静态)初始化函数( <clinit> )或者实例初始化(构造函数 /<init> )函数)。因此在我的逻辑思维中,在源代码中,初始化函数应该可以改变字段初始化中的值,这样还就可以在字段初始化中提供一个初始值,而在初始化函数中根据需要改变它。然而另我感到意外的是 Java 中只有实例初始化机制是这样实现的,而静态字段初始化中没有实现这种机制(在 C# 中不管实例初始化和静态初始化都实现了这种机制),静态字段初始化的顺序是完全根据源代码中定义顺序来初始化的;从耦合的角度,这就是一个顺序耦合的典型。不知道为什么 Java 要这样实现,是否它有其他方面的问题的考虑?亦或是 Java 设计者或者 Java 编译器设计者的一个失误?不管怎么样,用 sun 的 javac 编译出来的以上程序的运行结果如下:
staticIntVar: 20
staticComputeIntVar: 7
staticStrVar: Static block init(before)
staticRefVar: java.lang.Object@14318bb
staticAfterIntVar: 30
staticAfterStrVar: Static field init(after)
----------------- Instance Fields ---------
fieldIntVar : 200
fieldComputeIntVar : 8
fieldStrVar : Constructor field init(before)
fieldAfterIntVar : 400
fieldAfterStrVar : Constructor field init(after)
问题解释:
从以上程序生成的二进制代码就可以很好的解释以上的结果:
// staticIntVar = 10
0 bipush 10
2 putstatic org.levin.insidejvm.miscs.staticinit.StaticInitSequence.staticIntVar : int [ 22 ]
// staticComputeIntVar = (int)(Math.random() * 10)
5 invokestatic java.lang.Math.random() : double [ 24 ]
8 ldc2_w < Double 10.0 > [ 30 ]
11 dmul
12 d2i
13 putstatic org.levin.insidejvm.miscs.staticinit.StaticInitSequence.staticComputeIntVar : int [ 32 ]
// staticStrVar = “Static field init(before)”
16 ldc < String " Static field init(before) " > [ 34 ]
18 putstatic org.levin.insidejvm.miscs.staticinit.StaticInitSequence.staticStrVar : java.lang.String [ 36 ]
// staticRefVar = new Object();
21 new java.lang.Object [ 3 ]
24 dup
25 invokespecial java.lang.Object() [ 38 ]
28 putstatic org.levin.insidejvm.miscs.staticinit.StaticInitSequence.staticRefVar : java.lang.Object [ 41 ]
// staticIntVar = 20
31 bipush 20
33 putstatic org.levin.insidejvm.miscs.staticinit.StaticInitSequence.staticIntVar : int [ 22 ]
// staticStrVar = “Static block init(before)”
36 ldc < String " Static block init(before) " > [ 43 ]
38 putstatic org.levin.insidejvm.miscs.staticinit.StaticInitSequence.staticStrVar : java.lang.String [ 36 ]
// staticAfterIntVar = 40
本文通过一个具体的Java程序示例,详细解析了Java中静态变量的初始化顺序,并探讨了其背后的原因。文章揭示了静态变量初始化不同于实例变量初始化的特点。
518

被折叠的 条评论
为什么被折叠?



