【问题标题】:Java initialization order issue, static vs instance fieldsJava初始化顺序问题,静态与实例字段
【发布时间】:2010-03-11 07:58:19
【问题描述】:

下面的程序打印:

my name is:null

my name is:null

Someclass static init

AFAIK 当一个类首次加载时,静态块和字段总是首先初始化,实例块和字段其次。因此变量“objectName1”和“objectName2”应该首先初始化,实例变量“list”其次......但输出显然与这个理论相矛盾......谁能解释程序行为(我不是在寻找对顺便说一句,设计本身)?

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);
    }
}

【问题讨论】:

    标签: java


    【解决方案1】:

    静态块被初始化顺序(所以你可以依赖上面的在下面的)。通过创建SomeClass 的实例作为SomeClass 中的第一个静态初始化程序,您将在静态初始化阶段强制实例初始化。

    所以你的代码执行的逻辑顺序是:

    • 加载类SomeClass,所有静态字段初始默认(0null等)
    • 开始静态初始化
    • 第一个静态初始化创建SomeClass 的实例
    • SomeClass 实例开始实例初始化,使用静态字段的当前值(所以objectName1objectName2null
    • 加载SomeObject类,所有静态字段初始默认(你没有)
    • SomeObject静态初始化(你没有)
    • 使用传入的null 值创建SomeObject 的实例
    • 继续静态初始化SomeClass,设置objectName1objectName2

    要使这项工作如您所料,只需将 objectName1objectName2 的 init 放在 instance 的 init 上方即可。

    【讨论】:

      【解决方案2】:

      按照建议移动这条线:

      private static final SomeClass  instance    = new SomeClass();
      

      在这些之后:

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

      应该解决问题。

      【讨论】:

        【解决方案3】:

        乍一看,我自己对这种行为感到非常惊讶,但转念一想,解释起来很简单:

        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.&lt;clinit&gt;(类初始化)内部,因此 SomeClass 未完全初始化。

        附带说明:实现单例模式的最佳方法是完全避免它。第二个最有可能是使用enum(至少它是 Josh-Bloch 批准的)

        class enum SomeClass {
            instance;
        
            // snip
        }
        

        【讨论】:

          【解决方案4】:

          首先执行的可能是instance 变量的静态初始化程序。这会导致列表使用(未初始化的)objectName1objectName2 变量进行初始化。之后,它会继续初始化objectName1objectName2

          如果你将instance 的声明移到SomeClass 的末尾,它可能会如你所愿。

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2020-01-31
            • 2010-12-02
            • 2017-05-31
            • 2010-09-25
            相关资源
            最近更新 更多