10

静态字段在定义或初始化之前不能被引用:

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,所以很明显我们已经做了参考,顺序很重要,没有警告或编译错误。

这段代码合法吗?

4

6 回答 6

1

在第一个块中,您不是在初始化之前引用静态字段,而是在定义之前引用它(编译器会告诉您)。这将起作用,例如:

static Integer j;
static final Integer i = j;

所有字段在未显式设置时都有默认值(对于对象,这是null引用,对于原语,这是相应的合理默认值,0,false等)。因此,字段总是被初始化,因此编译器不需要检查。

关于final:它实际上是为开发人员的利益而使用的修饰符。正如JSL 所述final在访问之前必须“明确分配”字段。这并不意味着否则它们将没有价值(如果不是final),它只是意味着编译器会保护避免在找不到该分配的情况下不显式分配它。

因此,对于非final字段,您绝对可以在显式赋值之前引用它们。只需粘贴上面的代码片段即可进行验证。

于 2013-09-11T10:56:33.680 回答
1

这段代码合法吗?大概。我不认为编译器的工作是分析您故意在对象实例化中对静态变量产生的副作用。

对同一类中其他静态变量的“在引用之前声明”的有限分析实际上只是针对最常见的嘘声的帮助,而不是针对间接错误的铁定保证。

static对于“引用前声明”分析的范围仅限于直接访问其他声明中的静态变量,我真的一点也不感到惊讶。这是一个简单而紧凑的分析,具有最小的复杂性和非常快的速度。

扩展它以考虑对象实例化和方法调用的副作用,OTOH,将需要静态程序分析的 20-1000 倍更大的权重和范围。静态分析可能需要访问整个编译后的程序代码,使用基于约束的计算来确定可能发生的情况,并可能在几分钟内获得运行时间。

鉴于这些选择,从 Java 语言设计者的角度来看,选择仅涵盖来自同一类中的字段的直接访问的简单分析是相当容易的。

于 2013-09-11T10:56:56.330 回答
0

是的,这是因为首先分配了所有变量的空间(并用 #0 初始化),然后所有初始化都按照代码的顺序完成。

意味着,如果您像这样更改代码:

    类静态初始化 {

    静态整数 j, k;
    静态最终整数 i = 5;

    静态最终对象 o = new Object() {{
        j = 我;
    }};

    静态最终对象 o2 = new Object() {{
        k = 我;
    }};
    }

结果是所有人的variables == 5

于 2013-09-11T10:58:24.737 回答
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;
        }
    }
}

当您引用iinsideFoo1时,i为空。但是,当你在里面引用它时,i就变成了。请注意,如果是编译常量(is not ),则为5。5Foo2iiintIntegerj

请参阅此相关问题:以静态方式创建对象

于 2013-09-11T11:01:04.637 回答
0

变量的规则与final其他规则完全不同。例如,对于final变量,编译器必须检查该变量是否确实被赋值过一次并且之后没有被赋值。

这是可能的,因为对final变量有很多限制(这几乎是 的重点final)。

因此,深入分析“这是在使用前分配的吗”只在最终变量的有限世界中真正起作用。

于 2013-09-11T11:03:31.663 回答
0

这是在JLS 8.3.2.3中定义的。

仅当成员是实例时,成员的声明才需要在使用之前以文本形式出现。

这就是为什么当你这样做时你会得到错误..

static Integer j = i; /* compile error */
static final Integer i = 5;

但是通过方法的访问不会以相同的方式检查。

于 2013-09-11T11:09:26.100 回答