在下面的代码中,当访问带有类名的静态变量时,它不会抛出前向引用错误但是在没有类名的情况下访问它。
为什么在使用班级名称访问时不会发生这种情况?
class Test{
static {
System.out.println(a); // shows error
a = 99; // and this line too doesn't give error
System.out.println(Test.a); // this line doesn't
}
static int a = 10;
static{
System.out.println(a);
}
}
答案 0 :(得分:6)
前向引用规则在JLS §8.3.3:
中定义使用类声明变量,其声明以文本形式出现在 有时限制使用,即使这些类变量在 范围(§6.3)。具体来说,如果所有的话都是编译时错误 以下是真实的:
出现类或接口C中的类变量声明 使用类变量后的文本;
在C的类变量初始值设定项中,use是一个简单的名称 或C的静态初始化程序;
使用不在作业的左侧;
C是封闭使用的最里面的类或接口。
所以,基本上你的第一个Sysout()
,满足上述所有4个条件,因此它是一个编译时错误。
在第二个Sysout()
中,您正在使用其限定名称访问a
,而不是简单名称,根据上述规则,该名称是允许的。
现在,原因是,当您访问Test.a
时,编译器确保已加载Test
类并且已初始化所有static
字段,因此它可以访问字段a
。但是在简单名称上访问a
时,编译器不确定a
的初始化程序是否已经运行,因为它可能仍在处理加载类。
考虑以下加载类的过程:
static
变量分配内存。此时,变量a
已分配内存(声明已完成)static
初始化程序按发生顺序运行。
Sysout(a);
。 a
尚未初始化,因此您无法访问它。 (误差)a = 99
。在这里,您实际上是在初始化变量a
。非常好。Sysout(Test.a)
- 对此的推理已在上面发布。编译器知道Test
已经加载。static int a = 10
。它会将a
重新初始化为10
。请记住,声明部分已经在第一步处理。答案 1 :(得分:1)
首先,让我们看看JLS对非法前向引用的看法。
使用类变量 ,其声明以文本形式出现在 有时限制使用,即使这些类变量在 范围(§6.3)。具体来说,如果所有的话都是编译时错误 以下是真实的:
出现类或接口C中的类变量声明 使用类变量后的文本;
在C的类变量初始值设定项中,use是一个简单的名称 或者是C的静态初始化器;
使用不在作业的左侧;
C是封闭使用的最里面的类或接口。
使用实例变量 ,其声明以文本方式显示 即使这些实例变量,有时也会限制使用 在范围内。具体来说,如果所有的话都是编译时错误 以下是真实的:
在类或接口C中声明实例变量 在使用实例变量后以文本方式显示;
在实例变量初始值设定项中使用是一个简单的名称 C或C的实例初始值设定项;
使用不在作业的左侧;
C是封闭使用的最里面的类或接口。
它定义了每个静态和实例变量的非法前向引用。但是,两者的定义似乎相同。由于你的问题是关于静态的,我们只会深入研究它。
1)对于静态变量,请参见declaration
并且initialization
。
static int a; //only declaration
static int b = 10; //both declaration and initialization
2)使用声明在使用后以文本形式出现的类变量有时受限制,即使这些类变量在范围内。
它向我们解释了什么?它说有些情况下我们可以使用静态变量,即使我们之后声明它们。在某些情况下,这是不允许的。那么,这是不允许的情况呢?
如果同时满足以下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
附加的类名,那么您might
会输入非法的问题转发参考,否则你是我的朋友。但只是这种情况不符合非法前向引用的条件,否则您的代码中的a=99
会立即给我们错误。要生成此错误,还有2个条件必须成立。如果以下2不符合条件,您可以像这样使用它。]
点c)
非常简单。你没有在任务的左手边使用?如果我们看a=99
,不要! System.out.println(a)
- 这甚至不是任务。因此,没有左手分配是真的。
点d)
也很简单。它只是告诉你他在定义中意味着哪个类/接口C.你的C =测试。
现在让我们重新审视您的代码。在每行代码中,我将评论True+False+True+false
这样的含义,即对于每一行,a),b),c),d)将给予每一行。好的?
class Test {
static {
System.out.println(a); // True + True + True +True ; Illegal forward reference
a = 99; // True + True + False +True ; No illegal forward reference
System.out.println(Test.a); // True + False + True + True No illegal forward reference
}
static int a = 10;
static {
System.out.println(a);
}
}
现在可能会想到的下一个问题是什么呢?如果我们在宣布它之前使用它将需要什么价值?
现在我们来看静态初始化的规则。在这里,我们将根据您的代码进行操作。
当加载类并且没有非法的前向引用时,所有静态变量都已初始化为默认值并存在于内存中。
现在,在进行静态初始化之后。
class Test {
static {
System.out.println(Test.a); // prints 0
a = 99; // a get 99
System.out.println(Test.a); // prints 99
}
static int a = 10;
static {
System.out.println(a); // prints 10
}
public static void main(String[] args) {
}
}
答案 2 :(得分:0)
类初始值设定项在加载类时运行,您无法确定何时发生这种情况。在类初始化程序中运行的代码应仅用于需要处理或“初始化”其他静态方法(或类本身)才能正常工作的内容。
此外,您正在引用尚未声明的变量。在您的类初始值设定项(第一个静态块)中,您指定了a = 99
,但尚未声明。如果您想要声明变量,然后在静态块中初始化它。
没有理由对您要发布的代码类型使用类初始值设定项。如果有的话,这应该是一个静态方法。
这是一个例子
class Test{
static int a = 10;
static void doSomething(){
System.out.println(a);
a = 99;
System.out.println(a);
}
}
然后在主要内容中,您可以拨打Test.doSomething();
答案 3 :(得分:0)
好吧,静态块
static {
System.out.println(a);
a = 99;
System.out.println(Test.a);
}
将在声明a
之前执行。
带有Test.a
的行不会触发任何错误,因为编译器会检查并找到在类a
中声明的静态变量Test
。