上善若水
In general the OO style is to use a lot of little objects with a lot of little methods that give us a lot of plug points for overriding and variation. To do is to be -Nietzsche, To bei is to do -Kant, Do be do be do -Sinatra
posts - 146,comments - 147,trackbacks - 0

问题重现

让我们先来看一下以下的程序:

 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编译器设计者的一个失误?不管怎么样,用sunjavac编译出来的以上程序的运行结果如下:

----------------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日
注:这些文章都是前些时候写的,之前博客很乱,也都是随便贴一些自己写的或转载的,还有一些则是没有贴出来过的。现在打算好好整理一下,完整的记录自己的一些学习历程,而每次看到过去的时间,则让我想起以前的日子,因而我对时间一直是很重视的,所以每篇都著名写的日期,直到最先的文章出现。:)

posted on 2011-06-20 22:53 DLevin 阅读(2759) 评论(2)  编辑  收藏 所属分类: Core Java

FeedBack:
# re: Static变量和实例变量的初始化顺序问题
2011-06-21 09:54 | 成都制鞋培训
有点高级,看不懂  回复  更多评论
  
# re: Static变量和实例变量的初始化顺序问题
2011-06-21 22:41 | CodeMe
不错  回复  更多评论
  

只有注册用户登录后才能发表评论。


网站导航: