有 Java 编程相关的问题?

你可以在下面搜索框中键入要查询的问题!

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

当一个类本身有一个静态实例时,我很难理解初始化顺序。还有,为什么这种行为对于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) 个答案

  1. # 1 楼答案

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

    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.

    因此,INSTANCElistmapname之前以文本形式列在第一位。为什么不是所有3个都是null?这是因为name由一个常量表达式初始化;它是一个常量变量。它首先被初始化,在INSTANCE之前,因为它是一个常量变量

    请注意,您可以将初始化INSTANCE的行移动到listmap之后,导致listmapINSTANCE之前初始化

  2. # 2 楼答案

    在编译时,只有基元类型和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是最终原语,fScS是常量最终字符串,只有这三个字段是预先指定的

  3. # 3 楼答案

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

    if (name != null)
    

    汇编后将成为:

    if ("hello" != null)
    

    这当然是真的

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