以下内容无法编译,提供“非法转发参考”消息:
class StaticInitialisation {
static
{
System.out.println("Test string is: " + testString);
}
private static String testString;
public static void main(String args[]) {
new StaticInitialisation();
}
}
但是,以下编译:
class InstanceInitialisation1 {
{
System.out.println("Test string is: " + this.testString);
}
private String testString;
public static void main(String args[]) {
new InstanceInitialisation1();
}
}
但以下内容未编译,提供“非法转发参考”消息:
class InstanceInitialisation2 {
private String testString1;
{
testString1 = testString2;
}
private String testString2;
public static void main(String args[]) {
new InstanceInitialisation2();
}
}
为什么StaticInitialisation和InstanceInitialisation2不能编译,而InstanceInitialisation1呢?
答案 0 :(得分:57)
这由JLS的8.3.3部分涵盖:
使用类变量,其声明在使用后以文本形式出现有时受到限制,即使这些类变量在范围内(第6.3节)。具体来说,如果满足以下所有条件,则为编译时错误:
在使用类变量后,类或接口C中的类变量声明以文本形式出现;
在C的类变量初始值设定项或C的静态初始值设定项中,use是一个简单的名称;
使用不在作业的左侧;
C是封闭使用的最里面的类或接口。
使用在使用后以声明方式显示声明的实例变量有时会受到限制,即使这些实例变量在范围内也是如此。具体来说,如果满足以下所有条件,则为编译时错误:
在使用实例变量后,类或接口C中的实例变量声明以文本形式出现;
在C的实例变量初始值设定项或C的实例初始值设定项中,use是一个简单的名称;
使用不在作业的左侧;
C是封闭使用的最里面的类或接口。
在第二种情况下,使用不是一个简单的名称 - 你明确地得到了this
。这意味着它不符合上面引用的第二个列表中的第二个项目符号,因此没有错误。
如果您将其更改为:
System.out.println("Test string is: " + testString);
...然后就不会编译了。
或者在相反方向,您可以将静态初始化程序块中的代码更改为:
System.out.println("Test string is: " + StaticInitialisation.testString);
奇怪,但这就是它的方式。
答案 1 :(得分:2)
让我们看看这两个例子,我想这会让你清楚。
public class InstanceAndSataticInit {
{
System.out.println("Test string is (instance init): " + this.testString);
}
static{
System.out.println("Test string is (static init ): " + InstanceAndSataticInit.testStringStatic);
}
public static String testStringStatic="test";
public String testString="test";
public static void main(String args[]) {
new InstanceAndSataticInit();
}
}
输出:
Test string is (static init ): null
Test string is (instance init): null
并且
public class InstanceAndSataticInitVariableFirst {
public static String testStringStatic="test";
public String testString="test";
{
System.out.println("Test string is (instance init): " + this.testString);
}
static{
System.out.println("Test string is (static init ): " + InstanceAndSataticInitVariableFirst.testStringStatic);
}
public static void main(String args[]) {
new InstanceAndSataticInitVariableFirst();
}
}
输出:
Test string is (static init ): test
Test string is (instance init): test
所以你可以说序列是这样的。
将创建静态变量但不会初始化。
静态初始化将根据给定的顺序执行。
按顺序我的意思是在代码中出现。
我想这些步骤可以回答您的两个无效示例 StaticInitialisation
和 InstanceInitialisation2
但是,如果您的第二个工作示例 InstanceInitialisation1
使用this
关键字,那么您实际上是在帮助编译器忽略文本层次结构。当我在第一个例子中呼叫static
InstanceAndSataticInit
InstanceAndSataticInit.testStringStatic
发生同样的事情
答案 2 :(得分:2)
原因很简单 - 分析和禁止所有前向引用都太昂贵或不可能。 e.g。
{
print( getX(); ); // this.x
print( that().x ); // this.x
}
int x;
int getX(){ return x; }
This that(){ return this; }
该规范决定禁止一些表明常见程序员错误的简单案例。
答案 3 :(得分:1)
我们必须了解的是,在第二个代码段中,您正在使用块和此关键字。