以下程序打印:
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);
}
}
答案 0 :(得分:10)
静态块按顺序初始化(因此您可以依赖上面的那些)。通过在SomeClass
中创建SomeClass
实例作为第一个静态初始值设定项,您将在静态初始化阶段强制实例初始化。
因此,代码执行的逻辑顺序为:
SomeClass
,所有静态字段最初默认为(0
,null
等。)SomeClass
SomeClass
实例的实例内容(因此objectName1
和objectName2
为null
)SomeObject
类,所有静态字段最初默认(您没有)SomeObject
静态内容(您没有任何内容)SomeObject
值null
的实例
SomeClass
的静态内容,设置objectName1
和objectName2
为了让您的工作符合您的预期,只需将objectName1
和objectName2
的内容放在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
变量的静态初始值设定项。这会导致使用(未初始化的)objectName1
和objectName2
变量初始化列表。之后,它会继续初始化objectName1
和objectName2
。
如果您将instance
的声明移到SomeClass
的末尾,它可能会执行您期望的操作。