程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> C語言 >> 關於C語言 >> Static變量和實例變量的初始化順序問題

Static變量和實例變量的初始化順序問題

編輯:關於C語言

問題重現

讓我們先來看一下以下的程序:


 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.

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved