上善若水
          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 閱讀(2777) 評論(2)  編輯  收藏 所屬分類: Core Java

          FeedBack:
          # re: Static變量和實例變量的初始化順序問題
          2011-06-21 09:54 | 成都制鞋培訓
          有點高級,看不懂  回復  更多評論
            
          # re: Static變量和實例變量的初始化順序問題
          2011-06-21 22:41 | CodeMe
          不錯  回復  更多評論
            
          主站蜘蛛池模板: 松溪县| 高阳县| 正镶白旗| 漳浦县| 海原县| 南江县| 保靖县| 木兰县| 蒙山县| 云安县| 扎囊县| 花莲县| 郑州市| 大英县| 富川| 清徐县| 大方县| 肇庆市| 海南省| 盐亭县| 成都市| 广州市| 凤阳县| 贵德县| 锦州市| 张北县| 察隅县| 扶绥县| 鸡西市| 习水县| 太仓市| 彰化市| 色达县| 新营市| 五莲县| 云林县| 榆树市| 舞钢市| 宁陵县| 紫阳县| 集安市|