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
,所有静态字段最初默认为(0
,null
等) - 开始静态
- 第一个静态init创建
SomeClass
实例 - 使用静态字段的当前值开始
SomeClass
实例的实例内容(因此objectName1
和objectName2
为null
) - 加载
SomeObject
类,所有静态字段最初默认(你没有) - 做
SomeObject
静态SomeObject
(你没有) - 使用传入的
null
值创建SomeObject
实例 - 继续
SomeClass
静态内容,设置objectName1
和objectName2
为了使这个工作正如您所期望的那样,只需将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
变量的静态初始化程序。 这会导致使用(未初始化的) objectName1
和objectName2
变量初始化列表。 之后,它继续初始化objectName1
和objectName2
。
如果将instance
的声明移动到SomeClass
的末尾,它可能会执行您期望的操作。