有 Java 编程相关的问题?

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

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

下面的程序打印:

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

共 (4) 个答案

  1. # 1 楼答案

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

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

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

    要使这项工作如您所料,只需将objectName1objectName2的init置于instance的init之上

  2. # 2 楼答案

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

    如果你把instance的声明移到SomeClass的末尾,它可能会达到你所期望的效果

  3. # 3 楼答案

    乍一看,我对自己的行为感到非常惊讶,但再想想,要解释这一点很简单:

    private static final SomeClass instance = new SomeClass();
    

    SomeClass静态初始化的一部分。在初始化完成之前创建实例时,该类尚未完全初始化。当您用类似new Exception().printStackTrace();的东西替换System.out.println(...);时,您会得到这样的结果(注意,我将所有类作为静态嵌套类放入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没有完全初始化

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

    class enum SomeClass {
        instance;
    
        // snip
    }
    
  4. # 4 楼答案

    正如建议移动这条线:

    private static final SomeClass  instance    = new SomeClass();
    

    在这些之后:

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

    应该可以解决这个问题