当一个类有自己的静态实例时,我无法理解初始化顺序。此外,为什么String
的这种行为似乎有所不同。
请参阅以下示例:
public class StaticCheck {
private static StaticCheck INSTANCE = new StaticCheck();
private static final List<String> list =
new ArrayList<String>(Arrays.asList("hello"));
private static final Map<String, String> map =
new HashMap<String, String>();
private static final String name = "hello";
public static StaticCheck getInstance() {
return INSTANCE;
}
private StaticCheck() {
load();
}
private void load() {
if(list != null) {
System.out.println("list is nonnull");
} else {
System.out.println("List is null");
}
if(name != null) {
System.out.println("name is nonnull");
} else {
System.out.println("name is null");
}
if(map != null) {
System.out.println("Map is nonnull");
} else {
System.out.println("Map is null");
}
}
public static void main(String[] args) {
StaticCheck check = StaticCheck.getInstance();
}
}
输出:
List is null
name is nonnull
Map is null
我绝对不清楚为什么字段name
不为空。
在类初始化中提到的以下情况下初始化静态字段:
http://javarevisited.blogspot.in/2012/07/when-class-loading-initialization-java-example.html
看看上面的例子,我的想法是:
如上所述,静态字段在Java中的实例初始化之前初始化。在这里,当我调用静态方法getInstance()
时,它将导致类初始化,这意味着静态字段的初始化。在这种情况下,字段map
和list
不应为空。
在上面的示例中,由于字段INSTANCE
是静态的,因此在未初始化其他字段时,会发生对象初始化并且其构造函数会调用load()
。因此,字段list
和map
为空。那么为什么name
被初始化了呢?我有点困惑。
答案 0 :(得分:6)
String
类型name
变量是编译时常量,编译器在编译时将其内联。所以,条件是:
if (name != null)
编译后将成为:
if ("hello" != null)
这当然是真的。
至于为什么map
和list
是null
,这是因为,当初始化类时,INSTANCE
字段被初始化,调用构造函数,而构造函数又调用load()
方法。请注意,到目前为止,尚未运行其他static
初始值设定项。因此,map
和list
仍为null
。因此,使用load()
方法打印它们将是null
。
答案 1 :(得分:4)
在初始化任何非static
变量之前,会初始化常量static
变量。 JLS, Section 12.4.2表示类的初始化过程:
- 否则,记录当前线程正在进行C的Class对象初始化并释放LC的事实。 然后,初始化C的静态字段,它们是常量变量(§4.12.4,§8.3.2,§9.3.1)。
醇>(此处的其他步骤)
- 接下来,按文本顺序执行类的类变量初始值设定项和静态初始值设定项,或接口的字段初始值设定项,就好像它们是单个块一样。
醇>
因此,在INSTANCE
,list
和map
之前,name
首先按文字列出。为什么不是所有3个仍然null
?这是因为name
由常量表达式初始化;它是常量变量。它首先在INSTANCE
之前初始化,因为它是一个常量变量。
请注意,您可以在INSTANCE
和list
之后移动初始化map
的行,从而在list
之前初始化map
和INSTANCE
答案 2 :(得分:0)
仅在编译时分配基本类型和String
,并且仅当字段为final
并且使用文字或常量表达式初始化时。所有其他static
字段和块将在以后按顺序进行评估。见这个例子:
public class StaticExample {
private static StaticExample instance1 = new StaticExample(1);
public static void main(String[] args) {
new StaticExample(3);
}
public static boolean b = true;
public static final boolean fb = true;
public static Boolean B = true;
public static final Boolean fB = true;
public static String S = "text";
public static final String fS = "text";
public static final String cS = "te" + "xt"; // constant expression
public static final String xS = fS.substring(0, 2) + fS.substring(2, 4);
private static StaticExample instance2 = new StaticExample(2);
private StaticExample(int no) {
System.out.println("## " + no + ": ##");
System.out.println(" b: " + b);
System.out.println("fb: " + fb);
System.out.println(" B: " + B);
System.out.println("fB: " + fB);
System.out.println(" S: " + S);
System.out.println("fS: " + fS);
System.out.println("cS: " + cS);
System.out.println("xS: " + xS);
System.out.println();
}
}
<强>输出:强>
## 1: ##
b: false
fb: true
B: null
fB: null
S: null
fS: text
cS: text
xS: null
## 2: ##
b: true
fb: true
B: true
fB: true
S: text
fS: text
cS: text
xS: text
## 3: ##
b: true
fb: true
B: true
fB: true
S: text
fS: text
cS: text
xS: text
fb
是最终原语,fS
是最终字符串,只有这两个字段是预先分配的。