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

以下程序打印:

my name is:null my name is:null Someclass static init 

首次加载类时的AFAIK静态块和字段始终首先初始化,实例块和字段排在第二位。 因此变量“objectName1”和“objectName2”应首先初始化,实例变量“list”second …但是输出明显与此理论相矛盾…任何人都可以解释程序行为(我不是在寻找对它的批评设计本身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 list= new ArrayList () { { 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); } } 

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

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

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

为了使这个工作正如您所期望的那样,只需将objectName1和objectName2放在init之上。

正如建议移动这一行:

 private static final SomeClass instance = new SomeClass(); 

在这之后:

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

应该解决问题。

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

 private static final SomeClass instance = new SomeClass(); 

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

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

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

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

 class enum SomeClass { instance; // snip } 

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

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