类中的静态字段初始化序列

时间:2015-04-13 17:06:20

标签: java static

当一个类有自己的静态实例时,我无法理解初始化顺序。此外,为什么String的这种行为似乎有所不同。

请参阅以下示例:

public class StaticCheck {
    private static StaticCheck INSTANCE = new StaticCheck();    

    private static final List<String> list =
        new ArrayList<String>(Arrays.asList("hello"));
    private static final Map<String, String> map =
        new HashMap<String, String>();  
    private static final  String name = "hello";

    public static StaticCheck getInstance() {
        return INSTANCE;
    }

    private StaticCheck() {
        load();     
    }

    private void load() {
        if(list != null) {
            System.out.println("list is nonnull");
        } else {
            System.out.println("List is null");
        }
        if(name != null) {
            System.out.println("name is nonnull");
        } else {
            System.out.println("name is null");
        }
        if(map != null) {
            System.out.println("Map is nonnull");
        } else {
            System.out.println("Map is null");
        }
    }

    public static void main(String[] args) {
        StaticCheck check = StaticCheck.getInstance();
    }
}

输出:

List is null
name is nonnull
Map is null

我绝对不清楚为什么字段name不为空。 在类初始化中提到的以下情况下初始化静态字段: http://javarevisited.blogspot.in/2012/07/when-class-loading-initialization-java-example.html

看看上面的例子,我的想法是:

  1. 如上所述,静态字段在Java中的实例初始化之前初始化。在这里,当我调用静态方法getInstance()时,它将导致类初始化,这意味着静态字段的初始化。在这种情况下,字段maplist不应为空。

  2. 在上面的示例中,由于字段INSTANCE是静态的,因此在未初始化其他字段时,会发生对象初始化并且其构造函数会调用load()。因此,字段listmap为空。那么为什么name被初始化了呢?我有点困惑。

3 个答案:

答案 0 :(得分:6)

String类型name变量是编译时常量,编译器在编译时将其内联。所以,条件是:

if (name != null)
编译后

将成为:

if ("hello" != null)

这当然是真的。

至于为什么maplistnull,这是因为,当初始化类时,INSTANCE字段被初始化,调用构造函数,而构造函数又调用load()方法。请注意,到目前为止,尚未运行其他static初始值设定项。因此,maplist仍为null。因此,使用load()方法打印它们将是null

答案 1 :(得分:4)

在初始化任何非static变量之前,会初始化常量static变量。 JLS, Section 12.4.2表示类的初始化过程:

  
      
  1. 否则,记录当前线程正在进行C的Class对象初始化并释放LC的事实。   然后,初始化C的静态字段,它们是常量变量(§4.12.4,§8.3.2,§9.3.1)。
  2.         

    (此处的其他步骤)

         
        
    1. 接下来,按文本顺序执行类的类变量初始值设定项和静态初始值设定项,或接口的字段初始值设定项,就好像它们是单个块一样。
    2.   

因此,在INSTANCElistmap之前,name首先按文字列出。为什么不是所有3个仍然null?这是因为name常量表达式初始化;它是常量变量。它首先在INSTANCE之前初始化,因为它是一个常量变量。

请注意,您可以在INSTANCElist之后移动初始化map的行,从而在list之前初始化mapINSTANCE

答案 2 :(得分:0)

仅在编译时分配基本类型和String,并且仅当字段为final并且使用文字或常量表达式初始化时。所有其他static字段和块将在以后按顺序进行评估。见这个例子:

public class StaticExample {

    private static StaticExample instance1 = new StaticExample(1);

    public static void main(String[] args) {
        new StaticExample(3);
    }

    public static boolean b = true;

    public static final boolean fb = true;

    public static Boolean B = true;

    public static final Boolean fB = true;

    public static String S = "text";

    public static final String fS = "text";

    public static final String cS = "te" + "xt"; // constant expression

    public static final String xS = fS.substring(0, 2) + fS.substring(2, 4);

    private static StaticExample instance2 = new StaticExample(2);

    private StaticExample(int no) {
        System.out.println("## " + no + ": ##");
        System.out.println(" b: " + b);
        System.out.println("fb: " + fb);
        System.out.println(" B: " + B);
        System.out.println("fB: " + fB);
        System.out.println(" S: " + S);
        System.out.println("fS: " + fS);
        System.out.println("cS: " + cS);
        System.out.println("xS: " + xS);
        System.out.println();
    }

}

<强>输出:

## 1: ##
 b: false
fb: true
 B: null
fB: null
 S: null
fS: text
cS: text
xS: null

## 2: ##
 b: true
fb: true
 B: true
fB: true
 S: text
fS: text
cS: text
xS: text

## 3: ##
 b: true
fb: true
 B: true
fB: true
 S: text
fS: text
cS: text
xS: text

fb是最终原语,fS是最终字符串,只有这两个字段是预先分配的。