为什么通过 returns 另一个静态变量的方法调用初始化的静态变量仍然为空?
Why does a static variable initialized by a method call that returns another static variable remains null?
我根本看不懂下面代码的执行流程:
class Test {
static String s1 = getVal();
static String s2 = "S2";
private static String getVal() {
return s2;
}
public static void main(String args[]) {
System.out.println(s2); // prints S2
System.out.println(s1); // prints null
}
}
它应该在第二个 println
语句中打印 S2
。我更感兴趣的是理解为什么会发生这种情况而不是解决方案。
static
变量和 static
初始化程序块按照它们在源代码中出现的顺序进行初始化(static final
变量除外,这些变量在非最终 static
变量)。
s1
是在s2
之前初始化的,因此调用getVal()
returns默认值为s2
,即null
.
您可以更改 static
变量的顺序,以便首先初始化 s2
:
static String s2 = "S2";
static String s1 = getVal();
另一种强制 s2
的初始化发生在 s1
之前的方法是使 s2
最终:
static String s1 = getVal();
static final String s2 = "S2";
以下是 JLS 12.4.2. Detailed Initialization Procedure 中所述的初始化顺序的摘录。与 static
个变量相关的部分已突出显示。
For each class or interface C, there is a unique initialization lock LC. The mapping from C to LC is left to the discretion of the Java Virtual Machine implementation. The procedure for initializing C is then as follows:
Synchronize on the initialization lock, LC, for C. This involves waiting until the current thread can acquire LC.
If the Class object for C indicates that initialization is in progress for C by some other thread, then release LC and block the current thread until informed that the in-progress initialization has completed, at which time repeat this step.
If the Class object for C indicates that initialization is in progress for C by the current thread, then this must be a recursive request for initialization. Release LC and complete normally.
If the Class object for C indicates that C has already been initialized, then no further action is required. Release LC and complete normally.
If the Class object for C is in an erroneous state, then initialization is not possible. Release LC and throw a NoClassDefFoundError.
Otherwise, record the fact that initialization of the Class object for C is in progress by the current thread, and release LC.
Then, initialize the static fields of C which are constant variables(§4.12.4, §8.3.2, §9.3.1).
Next, if C is a class rather than an interface, then let SC be its superclass and let SI1, ..., SIn be all superinterfaces of C that declare at least one default method. The order of superinterfaces is given by a recursive enumeration over the superinterface hierarchy of each interface directly implemented by C (in the left-to-right order of C's implements clause). For each interface I directly implemented by C, the enumeration recurs on I's superinterfaces (in the left-to-right order of I's extends clause) before returning I.
For each S in the list [ SC, SI1, ..., SIn ], if S has not yet been initialized, then recursively perform this entire procedure for S. If necessary, verify and prepare S first.
If the initialization of S completes abruptly because of a thrown exception, then acquire LC, label the Class object for C as erroneous, notify all waiting threads, release LC, and complete abruptly, throwing the same exception that resulted from initializing S.
Next, determine whether assertions are enabled (§14.10) for C by querying its defining class loader.
Next, execute either the class variable initializers and static initializers of the class, or the field initializers of the interface, in textual order, as though they were a single block.
静态事物按照它们在代码中出现的顺序执行。
static String s1 = getVal();
所以从第一行开始,s1
得到评估,到那时 s2
仍然是 null
。因此您会看到空值。
根据 JLS 第 12.4.2 节,静态字段初始化如下:
Next, execute either the class variable initializers and static initializers of the class, or the field initializers of the interface, in textual order, as though they were a single block.
所以s1
先初始化,此时s2
没有初始化,所以默认值为String
,即null
。
然后 s2
初始化为 "S2"
但 s1
保持 null
。
只需更改两个声明的顺序即可解决此问题。
s1先初始化,此时s2的值为null。
s1 正在尝试 return 稍后初始化的 s2 的值。
这就是为什么您将其设置为空的原因。
如果你这样尝试,你会得到预期的答案'S2'
class Test {
static String s2 = "S2";
static String s1 = getVal();
private static String getVal() {
return s2;
}
public static void main(String args[]) {
System.out.println(s2); // prints S2
System.out.println(s1); // prints S2
}
}
我根本看不懂下面代码的执行流程:
class Test {
static String s1 = getVal();
static String s2 = "S2";
private static String getVal() {
return s2;
}
public static void main(String args[]) {
System.out.println(s2); // prints S2
System.out.println(s1); // prints null
}
}
它应该在第二个 println
语句中打印 S2
。我更感兴趣的是理解为什么会发生这种情况而不是解决方案。
static
变量和 static
初始化程序块按照它们在源代码中出现的顺序进行初始化(static final
变量除外,这些变量在非最终 static
变量)。
s1
是在s2
之前初始化的,因此调用getVal()
returns默认值为s2
,即null
.
您可以更改 static
变量的顺序,以便首先初始化 s2
:
static String s2 = "S2";
static String s1 = getVal();
另一种强制 s2
的初始化发生在 s1
之前的方法是使 s2
最终:
static String s1 = getVal();
static final String s2 = "S2";
以下是 JLS 12.4.2. Detailed Initialization Procedure 中所述的初始化顺序的摘录。与 static
个变量相关的部分已突出显示。
For each class or interface C, there is a unique initialization lock LC. The mapping from C to LC is left to the discretion of the Java Virtual Machine implementation. The procedure for initializing C is then as follows:
Synchronize on the initialization lock, LC, for C. This involves waiting until the current thread can acquire LC.
If the Class object for C indicates that initialization is in progress for C by some other thread, then release LC and block the current thread until informed that the in-progress initialization has completed, at which time repeat this step.
If the Class object for C indicates that initialization is in progress for C by the current thread, then this must be a recursive request for initialization. Release LC and complete normally.
If the Class object for C indicates that C has already been initialized, then no further action is required. Release LC and complete normally.
If the Class object for C is in an erroneous state, then initialization is not possible. Release LC and throw a NoClassDefFoundError.
Otherwise, record the fact that initialization of the Class object for C is in progress by the current thread, and release LC.
Then, initialize the static fields of C which are constant variables(§4.12.4, §8.3.2, §9.3.1).
Next, if C is a class rather than an interface, then let SC be its superclass and let SI1, ..., SIn be all superinterfaces of C that declare at least one default method. The order of superinterfaces is given by a recursive enumeration over the superinterface hierarchy of each interface directly implemented by C (in the left-to-right order of C's implements clause). For each interface I directly implemented by C, the enumeration recurs on I's superinterfaces (in the left-to-right order of I's extends clause) before returning I.
For each S in the list [ SC, SI1, ..., SIn ], if S has not yet been initialized, then recursively perform this entire procedure for S. If necessary, verify and prepare S first.
If the initialization of S completes abruptly because of a thrown exception, then acquire LC, label the Class object for C as erroneous, notify all waiting threads, release LC, and complete abruptly, throwing the same exception that resulted from initializing S.
Next, determine whether assertions are enabled (§14.10) for C by querying its defining class loader.
Next, execute either the class variable initializers and static initializers of the class, or the field initializers of the interface, in textual order, as though they were a single block.
静态事物按照它们在代码中出现的顺序执行。
static String s1 = getVal();
所以从第一行开始,s1
得到评估,到那时 s2
仍然是 null
。因此您会看到空值。
根据 JLS 第 12.4.2 节,静态字段初始化如下:
Next, execute either the class variable initializers and static initializers of the class, or the field initializers of the interface, in textual order, as though they were a single block.
所以s1
先初始化,此时s2
没有初始化,所以默认值为String
,即null
。
然后 s2
初始化为 "S2"
但 s1
保持 null
。
只需更改两个声明的顺序即可解决此问题。
s1先初始化,此时s2的值为null。 s1 正在尝试 return 稍后初始化的 s2 的值。 这就是为什么您将其设置为空的原因。
如果你这样尝试,你会得到预期的答案'S2'
class Test {
static String s2 = "S2";
static String s1 = getVal();
private static String getVal() {
return s2;
}
public static void main(String args[]) {
System.out.println(s2); // prints S2
System.out.println(s1); // prints S2
}
}