可以将文章内容翻译成中文,广告屏蔽插件可能会导致该功能失效(如失效,请关闭广告屏蔽插件后再试):
问题:
A static field cannot be referenced before it is defined or initialized:
static Integer j = i; /* compile error */
static final Integer i = 5;
However, when it is referenced from an instance initialization block (in an anonymous inner class), not even a warning is generated.
See example:
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;
}};
}
The result is: j == null
, k == 5
, so clearly we've made a reference, order matters, and no warning or compilation error.
Is this code legal?
回答1:
Is this code legal? Probably. I don't think it's the compiler's job to analyze your deliberate side-effects of object instantiation in buggering up static variables.
The limited analysis of 'declare before referencing' from other statics in the same class is really just a helper against the most common boo-boos, not a ironclad guarantee against indirect errors.
I'm really not at all surprised that the the "declare before referencing" analysis is limited in scope to direct access of static variables in other static
declarations. This is a simple & compact analysis with minimal complexity & very fast.
Extending it to consider side-effects of object instantiation & method calls, OTOH, would require the 20-1000x greater weight & scope of static program analysis. Static analysis requires access to potentially the entire compiled program code, with constraint-based computation to determine what may possibly happen, and run-times potentially in the minutes.
Given these choices it is fairly easy, in the Java language designer's shoes, to choose simple analysis covering only direct accesses from fields within the same class.
回答2:
In the first block, you're not referencing a static field before it is initialized, but referencing before it is defined (as the compiler will tell you). This will work, for example:
static Integer j;
static final Integer i = j;
All fields have default values when not set explicitly (for objects, that's the null
reference, for primitives, that's the corresponding sensible default, 0
, false
, etc.). Therefore, the fields are always initialized so the compiler doesn't need to check for that.
Regarding final
: it's really a modifier used for the benefit of the developer(s). As the JSL states, final
fields must be "definitely assigned" before access. That does not mean they would have no value otherwise (if it were not final
), it just means that the compiler protects you against not assigning it explicitly, if it cannot find that assignment.
So, for non-final
fields, you most definitely can reference them before an explicit assignment. Just paste the code snippet above to verify.
回答3:
Your code is similar to this one:
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;
}
}
}
When you refer to i
inside Foo1
, i
was null. However, i
becomes 5
when you refer to it inside Foo2
. Note that if i
was a compile-constant (i
is int
not Integer
), then j
would be 5.
See this related question: Creating an object in a static way
回答4:
The rules for final
variables are quite different from all others. For example, for final
variables the compiler must check if the variable has definitely been assigned once and not been assigned afterwards.
This is possible, because there are quite a few restrictions on final
variables (which is pretty much the point of final
).
Therefore, the in-depth analysis of "is this assigned before use" only really works within the limited world of final variables.
回答5:
This is defined in JLS 8.3.2.3.
The declaration of a member needs to appear textually before it is
used only if the member is an instance.
That is why you are getting error when you do this..
static Integer j = i; /* compile error */
static final Integer i = 5;
But accesses by methods are not checked in the same way.
回答6:
Yes, that happens because first the space for all variables is allocated (and initialized with #0) and after that all the initialization is done in the order of your code.
Means, if you change the code like this:
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;
}};
}
The result is for all variables == 5
.