问题重现
让我们先来看一下以下的程序:
1 public class StaticInitSequence { 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编译出来的以上程序的运行结果如下:
---------------- Static Fields ------------ 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)
问题解释:
从以上程序生成的二进制代码就可以很好的解释以上的结果:
< clinit > : // 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 41 bipush 40 43 putstatic org.levin.insidejvm.miscs.staticinit.StaticInitSequence.staticAfterIntVar : int [ 45 ] // staticAfterStr = “Statci block init(after)” 46 ldc < String " Static block init(after) " > [ 47 ] 48 putstatic org.levin.insidejvm.miscs.staticinit.StaticInitSequence.staticAfterStrVar : java.lang.String [ 49 ] // staticAfterIntVar = 30 51 bipush 30 53 putstatic org.levin.insidejvm.miscs.staticinit.StaticInitSequence.staticAfterIntVar : int [ 45 ] // staticAfterStrVar = “Static field init(after)” 56 ldc < String " Static field init(after) " > [ 51 ] 58 putstatic org.levin.insidejvm.miscs.staticinit.StaticInitSequence.staticAfterStrVar : java.lang.String [ 49 ] 61 return < init > : // invoke base constructor 0 aload_0 [ this ] 1 invokespecial java.lang.Object() [ 38 ] 4 aload_0 [ this ] // fieldIntVar = 100 5 bipush 100 7 putfield org.levin.insidejvm.miscs.staticinit.StaticInitSequence.fieldIntVar : int [ 55 ] // fieldComputeIntVar = (int)(Math.random() * 100) 10 aload_0 [ this ] 11 invokestatic java.lang.Math.random() : double [ 24 ] 14 ldc2_w < Double 100.0 > [ 57 ] 17 dmul 18 d2i 19 putfield org.levin.insidejvm.miscs.staticinit.StaticInitSequence.fieldComputeIntVar : int [ 59 ] // fieldStrVar = “Instance field init(before)” 22 aload_0 [ this ] 23 ldc < String " Instance field init(before) " > [ 61 ] 25 putfield org.levin.insidejvm.miscs.staticinit.StaticInitSequence.fieldStrVar : java.lang.String [ 63 ] // fieldAfterIntVar = 300 28 aload_0 [ this ] 29 sipush 300 32 putfield org.levin.insidejvm.miscs.staticinit.StaticInitSequence.fieldAfterIntVar : int [ 65 ] // fieldAfterStrVar = “Instance field init(after)” 35 aload_0 [ this ] 36 ldc < String " Instance field init(after) " > [ 67 ] 38 putfield org.levin.insidejvm.miscs.staticinit.StaticInitSequence.fieldAfterStrVar : java.lang.String [ 69 ] // fieldIntVar = 200 41 aload_0 [ this ] 42 sipush 200 45 putfield org.levin.insidejvm.miscs.staticinit.StaticInitSequence.fieldIntVar : int [ 55 ] // fieldStrVar = “Constructor field init(before)” 48 aload_0 [ this ] 49 ldc < String " Constructor field init(before) " > [ 71 ] 51 putfield org.levin.insidejvm.miscs.staticinit.StaticInitSequence.fieldStrVar : java.lang.String [ 63 ] // fieldAfterIntVar = 400 54 aload_0 [ this ] 55 sipush 400 58 putfield org.levin.insidejvm.miscs.staticinit.StaticInitSequence.fieldAfterIntVar : int [ 65 ] // fieldAfterStrVar = “Constructor field init(after)” 61 aload_0 [ this ] 62 ldc < String " Constructor field init(after) " > [ 73 ] 64 putfield org.levin.insidejvm.miscs.staticinit.StaticInitSequence.fieldAfterStrVar : java.lang.String [ 69 ] 67 return 问题延伸
在这里,细心的人可能还会想到另外一个问题,如果StaticInitSequence类还有父类,并且父类中同同时有静态成员初始化,静态语句块初始化,实例成员初始化,构造函数初始化,那会这样的顺序会是怎么样的呢?在Java中,保证父类的初始化要早于子类的初始化,因而如果有父类存在的话,一定是先父类初始化做好以后才做子类的初始化(这一点和C#又有略微的不同,在C#中,子类的字段初始化语句要早于父类的初始化语句和构造函数),并且是先静态初始化再实例初始化。
于2010年9月24日 注:这些文章都是前些时候写的,之前博客很乱,也都是随便贴一些自己写的或转载的,还有一些则是没有贴出来过的。现在打算好好整理一下,完整的记录自己的一些学习历程,而每次看到过去的时间,则让我想起以前的日子,因而我对时间一直是很重视的,所以每篇都著名写的日期,直到最先的文章出现。:)