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

Static field initialization sequence in a class

当 class 具有自身的静态实例时,我无法理解初始化顺序。此外,为什么这种行为对于 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 字段不为空。 Class 初始化中提到的静态字段在以下情况下被初始化: http://javarevisited.blogspot.in/2012/07/when-class-loading-initialization-java-example.html

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

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

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

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

if (name != null)

编译后会变成:

if ("hello" != null)

这当然是真的。

至于为什么maplistnull,那是因为在初始化class的时候,初始化了INSTANCE字段,调用构造函数,它又调用 load() 方法。请注意,此时,其他 static 个初始值设定项尚未 运行。所以,maplist 仍然是 null。因此,在 load() 方法中打印它们将是 null.

常量 static 变量在初始化任何非 static 变量之前被初始化。 JLS, Section 12.4.2 说明了 class:

的初始化过程
  1. Otherwise, record the fact that initialization of the Class object for C is in progress by the current thread, and release LC. Then, initialize the static fields of C which are constant variables (§4.12.4, §8.3.2, §9.3.1).

(other steps here)

  1. Next, execute either the class variable initializers and static initializers of the class, or the field initializers of the interface, in textual order, as though they were a single block.

因此,INSTANCE 按文本顺序排在第一位,排在 listmapname 之前。为什么不是所有 3 个都仍然 null?这是因为name是由一个常量表达式初始化的;它是一个 常量变量 。它首先被初始化,在 INSTANCE 之前,因为它是一个常量变量。

请注意,您可以将初始化 INSTANCE 的行移动到 listmap 之后,导致 listmap 在 [=12] 之前初始化=].

只有原始类型和 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是final原语,fScS是constant final String,只有这三个字段是预赋值的。