为什么这段代码sn -p不是编译错误,为什么初始化后变量没有设置为true?
如果在赋值语句的左侧使用静态变量,Java 允许前向引用。初始化后值为false的原因是,当Java到达静态初始化块时,变量STATIC已经被声明(发生在类的实际初始化之前)并初始设置为false(默认值)。在静态初始化器之后,声明行的赋值仍然被执行,值将结束为false。
在哪些情况下(通常)可以在 Java 中转发访问静态变量?
Java 8 的 Java 语言规范 (JLS) 在 12.4.1 When Initialization Occurs 中说:
静态初始化器和类变量初始化器是
按文本顺序执行,并且可能不引用类变量
在出现 声明的类中声明
使用后的文字,即使这些类变量在
范围(第 8.3.3 节)。
如前所述,一般来说,前向引用是不允许的。但是8.3.3 Forward References During Field Initialization中描述了某些例外情况:
8.3.3。字段初始化期间的前向引用
使用以文本形式声明的类变量
使用后有时会受到限制,即使这些类
变量在范围内(第 6.3 节)。具体来说,它是一个编译时
错误如果以下所有都为真:
- 在类或接口 C 中声明类变量
使用类变量后以文本形式出现
- 使用
是 C 的类变量初始值设定项中的简单名称
C的静态初始化器
- 使用不在左侧
赋值
- C是最里面的类或接口
封闭用途
为了说明这些条件,我创建了 ThisClass 类,并为每个条件提供了一个示例。如果这四个条件中只有一个为假,则不是编译错误!
1.类或接口 C 中的类变量声明在使用类变量后以文本形式出现
public static boolean STATIC1 = false;
static {
if (!STATIC1) {
System.out.println("Set STATIC1 to true");
STATIC1 = true;
}
}
STATIC1 的使用发生在它被定义之后。违反了编译器错误的最基本条件。如你所料,初始化后STATIC1的值为true。
2。用法是 C 的类变量初始化器或 C 的静态初始化器中的简单名称
static {
if (!ThisClass.STATIC2) {
System.out.println("Set STATIC2 to true");
ThisClass.STATIC2 = true;
}
}
public static boolean STATIC2 = false;
现在满足第一个条件,但违反了第二个条件。我们使用限定名(而不是简单名)来访问变量STATIC2。区别在6.2 Names and Identifiers 中的 JLS 中进行了描述:
名称有两种形式:简单名称和限定名称。
simple name 是单个标识符。
qualified name
由一个名称、一个“.”组成。令牌和标识符。
更有趣的是,初始化期间实际发生了什么。在静态初始化程序的第一行,STATIC2 的值是通过类限定符读取的。此时(因为类是按文本顺序初始化的)变量STATIC2 尚未初始化。在这种情况下,使用默认值,对于boolean,默认值是false。输入if 块并将STATIC2 的值设置为true。继续初始化,STATIC2 再次设置为false,这是变量初始化后的状态。
注意:为了证明对true 的赋值确实发生了,你可以尝试这样的事情:
static {
if (!ThisClass.STATIC2a) {
System.out.println("Set STATIC2a to true");
ThisClass.STATIC2a = true;
}
}
public static boolean STATIC2a;
这段代码编译得很好,初始化结束时STATIC2a的值将是true!最后一行代码甚至可以替换为以下奇怪的语句,它更清楚地说明了正在发生的事情:
public static boolean STATIC2a = ThisClass.STATIC2a;
3.使用不在作业的左侧
static {
System.out.println("Set STATIC3 to true");
STATIC3 = true;
}
public static boolean STATIC3 = false;
现在我们在声明之前使用类变量,并且使用一个简单的名称。但是,不满足 JLS 中的第三个条件,因为它出现在作业的左侧。发生的事情与访问合格的情况大致相同。 STATIC3 的默认值 false 被 true 覆盖,然后在最后一行代码中设置回 false。
注意: 与示例 2a 相同。如果最后一行代码没有赋值(或者STATIC3a = ThisClass.STATIC3a无操作),STATIC3a被初始化为true:
static {
System.out.println("Set STATIC3a to true");
STATIC3a = true;
}
public static boolean STATIC3a;
4. C 是包含使用的最内层类或接口
static class OtherClass {
static {
if (!STATIC4) {
System.out.println("Set STATIC4 to true");
STATIC4 = true;
}
}
}
public static boolean STATIC4 = false;
STATIC4 定义在外部类ThisClass 中,因此从OtherClass 的使用角度来看,没有定义在最内部的类中。静态块中的第一行满足上述所有三个条件。声明在使用后以文本形式出现,它由一个简单的名称引用,并且使用也不在赋值的左侧。但由于外部类总是在任何嵌套类之前初始化,ThisClass 中的STATIC4 = false 赋值发生在OtherClass 中的静态初始化之前。
重要的是要看到,OtherClass 的初始化不必在外部 ThisClass 初始化时发生(它甚至可能被加载,但正在加载!= 初始化)。我们需要确保 OtherClass 由例如初始化。创建它的一个实例(参见 JLS 中的12.4.1)。
整个程序可以在这里学习:
http://ideone.com/fl5Vet