在定义或初始化静态字段之前,不能引用静态字段:
static Integer j = i; /* compile error */
static final Integer i = 5;
但是,当它从实例初始化块(在匿名内部类中)引用时,甚至不会生成警告。
参见示例:
class StaticInitialization {
static final Object o = new Object() {{
j = i;
}};
static Integer j, k;
static final Integer i = 5;
static final Object o2 = new Object() {{
k = i;
}};
}
结果是:j == null
,k == 5
,所以很明显我们已经做了参考,订单很重要,没有任何警告或编译错误。
这段代码合法吗?
答案 0 :(得分:1)
在第一个块中,您在初始化之前没有引用静态字段,而是在已定义之前引用(如编译器将告诉您的那样)。这将起作用,例如:
static Integer j;
static final Integer i = j;
所有字段在未明确设置时具有默认值(对于对象,即null
引用,对于基元,这是相应的合理默认值,0
,false
等。因此,字段始终被初始化,因此编译器不需要检查它。
关于final
:它实际上是一个用于开发人员利益的修饰符。由于JSL states,final
字段必须在访问前“明确分配”。这并不意味着它们没有其他值(如果它不是final
),它只是意味着编译器保护你不显式分配它,如果它找不到该赋值。
因此,对于非final
字段,您绝对可以在明确分配之前引用它们。只需粘贴上面的代码段即可验证。
答案 1 :(得分:1)
此代码合法吗?大概。我认为编译器的工作并不是分析你在编写静态变量时对象实例化的故意副作用。
对同一类别中其他静力学的“在引用之前声明”的有限分析实际上只是对抗最常见的嘘声的帮助,而不是对间接错误的铁定保证。
我真的不会感到惊讶的是,“在引用前声明”分析的范围有限,无法在其他static
声明中直接访问静态变量。这是一个简单的&紧凑的分析,最小的复杂性和很快。
扩展它以考虑对象实例化的副作用&方法调用OTOH需要20-1000倍的重量和重量。 静态程序分析的范围。静态分析需要访问可能的整个编译程序代码,使用基于约束的计算来确定可能发生的情况,以及可能在几分钟内运行的时间。
鉴于这些选择,在Java语言设计师的帮助下,选择简单分析非常容易,只涵盖同一类中字段的直接访问。
答案 2 :(得分:0)
是的,这是因为首先分配所有变量的空间(并用#0初始化),然后按照代码的顺序完成所有初始化。
意思是,如果你改变这样的代码:
class StaticInitialization { static Integer j, k; static final Integer i = 5; static final Object o = new Object() {{ j = i; }}; static final Object o2 = new Object() {{ k = i; }}; }
结果适用于所有variables == 5
。
答案 3 :(得分:0)
您的代码与此类似:
class StaticInitialization
{
static final Foo1 o = new Foo1();
static Integer j, k;
static final Integer i = 5;
static final Foo2 o = new Foo2();
class Foo1
{
public Foo1()
{
j = i;
}
}
class Foo2
{
public Foo2()
{
k = i;
}
}
}
当您在i
内引用Foo1
时,i
为空。但是,i
在5
内引用Foo2
时会变为i
。请注意,如果i
是编译常量(int
是Integer
而非j
),那么{{1}}将为5。
请参阅此相关问题:Creating an object in a static way
答案 4 :(得分:0)
final
变量的规则与其他变量完全不同。例如,对于final
变量,编译器必须检查变量是否已明确分配一次,之后未分配。
这是可能的,因为对final
变量有很多限制(这几乎是final
的重点)。
因此,对“使用前指定”的深入分析只能在有限的最终变量世界中发挥作用。
答案 5 :(得分:0)
这在JLS 8.3.2.3中定义。
成员的声明需要以文本形式出现 仅在成员是实例时使用。
这就是你这样做时出错的原因..
static Integer j = i; /* compile error */
static final Integer i = 5;
但不会以同样的方式检查方法访问。