問題重現
讓我們先來看一下以下的程序:
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日
注:這些文章都是前些時候寫的,之前博客很亂,也都是隨便貼一些自己寫的或轉載的,還有一些則是沒有貼出來過的。現在打算好好整理一下,完整的記錄自己的一些學習歷程,而每次看到過去的時間,則讓我想起以前的日子,因而我對時間一直是很重視的,所以每篇都著名寫的日期,直到最先的文章出現。:)
posted on 2011-06-20 22:53
DLevin 閱讀(2775)
評論(2) 編輯 收藏 所屬分類:
Core Java