首先,让我们看看
JLS对于非法的前向引用有什么说法。
类变量的使用,即使这些类变量在作用域内(§6.3),但其声明出现在使用之后时,有时会受到限制。具体来说,如果以下所有条件都成立,则会出现编译时错误:
- 在类或接口C中,一个类变量的声明在使用该类变量之后;
- 使用是C的类变量初始化器或静态初始化器中的简单名称;
- 使用不在赋值的左侧;
- C是最内层包含使用的类或接口。
实例变量的使用,即使这些实例变量在作用域内,其声明出现在使用之后时,有时也会受到限制。具体来说,如果以下所有条件都成立,则会出现编译时错误:
- 在类或接口C中,一个实例变量的声明在使用该实例变量之后;
- 使用是C的实例变量初始化器或实例初始化器中的简单名称;
- 使用不在赋值的左侧;
- C是最内层包含使用的类或接口。
它为每个静态和实例变量定义了什么是非法的前向引用。然而,对于它们两者来说,定义似乎是相同的。由于您的问题涉及静态变量,我们只会深入探讨这个问题。
1)看到了吗,对于一个静态变量,有“声明”和“初始化”两个步骤。
static int a
static int b = 10
使用在使用之前文本上出现的类变量有时会受到限制,即使这些类变量在作用域内。这段话告诉我们什么?它说有时候即使我们在后面声明了静态变量,也可以使用它们。但在某些情况下,这是不允许的。那么,什么情况下是不允许的呢?
如果同时满足以下4个条件,就不允许使用,否则即使在声明之后也可以自由使用。
a) The declaration of a class variable in a class or interface C appears textually after a use of the class variable;
b) The use is a simple name in either a class variable initializer of C or a static initializer of C;
c) The use is not on the left hand side of an assignment;
d) C is the innermost class or interface enclosing the use.
好的,
a)
点很简单。它说当你在声明静态变量之前使用它时,你必须只看这些规则,否则为什么要深入研究JLS呢?
b)
点是,如果你使用简单名称,比如
boy
(而不是类名附加到它上面,比如
MyClass.boy
),那么你
可能
会遇到非法向前引用的问题,否则你是安全的,我的朋友。但是,仅仅满足这个条件并不符合非法向前引用的条件,否则你代码中的
a=99
会立即给我们报错。还有两个条件必须满足才能生成此错误。如果以下2个条件不符合,您可以像这样使用它。
c)
点非常直观。你没有在赋值的左边使用它吗?如果我们看看
a=99
,没有!!
System.out.println(a)
- 这甚至不是一个赋值语句。因此,没有左手赋值是正确的。
d)
点也很简单。它只告诉你在定义中他指的是哪个类/接口。你的C = Test。
现在让我们重新审视您的代码。对于每行代码,我会像这样评论`True+False+True+false`,意思是对于每行代码,a)、b)、c)、d)分别会给出什么评价。好吗?
class Test {
static {
System.out.println(a);
a = 99;
System.out.println(Test.a);
}
static int a = 10;
static {
System.out.println(a);
}
}
下一个问题可能是它会打印什么?如果我们在声明之前使用它,它会取什么值?
现在我们来讲解静态初始化的规则。在这里,我们将按照您的代码进行说明。
当类被加载且没有非法的向前引用时,所有静态变量都已被初始化为默认值并存在内存中。
现在,静态初始化完成后,程序继续执行。
class Test {
static {
System.out.println(Test.a);
a = 99;
System.out.println(Test.a);
}
static int a = 10;
static {
System.out.println(a);
}
public static void main(String[] args) {
}
}