Java初始化顺序问题,静态vs实例字段

时间:2010-03-11 07:58:20

标签: java

以下程序打印:

my name is:null

my name is:null

Someclass static init

首次加载类时的AFAIK静态块和字段总是先被初始化,实例块和字段是第二个。因此变量“objectName1”和“objectName2”应该首先被初始化,实例变量“list”第二......但是输出明显与这个理论相矛盾...任何人都可以解释程序行为(我不是在寻找对它的批评设计本身btw)?

import java.util.ArrayList;
import java.util.List;

public class Main2{
    public static void main (String[] args){
        SomeClass.getInstance();
    }
}

class SomeClass {
    private static final SomeClass instance = new SomeClass();

    public static SomeClass getInstance(){
        return instance;
    }

    static {
        System.out.println ("Someclass static init");
    }
    private  static String objectName1  ="test1";
    private  static String objectName2  ="test2";

    @SuppressWarnings("serial")
    private  List<SomeObject> list=
        new ArrayList<SomeObject> ()  { {
 add (new SomeObject(objectName1));
 add (new SomeObject(objectName2));
    }};
}

class SomeObject {
    String name;
    SomeObject (String name){
        this.name = name;
        System.out.println ("my name is:" +name);
    }
}

4 个答案:

答案 0 :(得分:10)

静态块按顺序初始化(因此您可以依赖上面的那些)。通过在SomeClass中创建SomeClass实例作为第一个静态初始值设定项,您将在静态初始化阶段强制实例初始化。

因此,代码执行的逻辑顺序为:

  • 加载班级SomeClass,所有静态字段最初默认为(0null等。)
  • 开始静态inits
  • 第一个静态init创建SomeClass
  • 的实例
  • 使用静态字段的当前值开始SomeClass实例的实例内容(因此objectName1objectName2null
  • 加载SomeObject类,所有静态字段最初默认(您没有)
  • 执行SomeObject静态内容(您没有任何内容)
  • 使用传入的SomeObject
  • 创建null的实例
  • 继续SomeClass的静态内容,设置objectName1objectName2

为了让您的工作符合您的预期,只需将objectName1objectName2的内容放在instance的init之上。

答案 1 :(得分:1)

正如建议移动这一行:

private static final SomeClass  instance    = new SomeClass();

之后:

private  static String objectName1  ="test1";
private  static String objectName2  ="test2";

应该解决问题。

答案 2 :(得分:1)

初看起来我对自己的行为感到非常惊讶,但是第二个想法,解释起来是非常简单的:

private static final SomeClass instance = new SomeClass();

SomeClass的静态初始化的一部分。在初始化完成之前创建实例时,该类尚未完全初始化。当您使用System.out.println(...);替换new Exception().printStackTrace();时,您会得到这个(请注意,我将所有类作为静态嵌套类放入Main)

at Main$SomeObject.<init>(Main.java:37) // new Exception().printStackTrace();
at Main$SomeClass$1.<init>(Main.java:26) // add(new SomeObject(...))
at Main$SomeClass.<init>(Main.java:23) // list = new ArrayList()
at Main$SomeClass.<clinit>(Main.java:10) // instance = new SomeClass()
at Main.main(Main.java:6) // SomeClass.getInstance();

如您所见,执行仍在Main$SomeClass.<clinit>内(类初始化),因此SomeClass未完全初始化。

作为旁注:实现Singleton模式的最佳方法是完全避免它。第二个最有可能是使用enum(至少是Josh-Bloch批准的)

class enum SomeClass {
    instance;

    // snip
}

答案 3 :(得分:0)

执行的第一件事可能是instance变量的静态初始值设定项。这会导致使用(未初始化的)objectName1objectName2变量初始化列表。之后,它会继续初始化objectName1objectName2

如果您将instance的声明移到SomeClass的末尾,它可能会执行您期望的操作。