为什么两个程序有前向引用错误,而第三个程序没有?

以下不会编译,给出一个“非法前向引用”的消息:

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呢?

JLS第8.3.3节涵盖了这一点:

尽pipe这些类variables在作用域(§6.3)中,但在使用后声明出现在文本上的类variables的使用有时会受到限制。 特别是,如果以下所有情况都是正确的,则是编译时错误:

  • 类或接口C中的类variables的声明在使用类variables之后以文本forms出现;

  • 在C的类variables初始值设定项或C的静态初始值设定项中,用法是简单的名称;

  • 使用不在作业的左侧;

  • C是封闭使用的最内层的类或接口。

尽pipe这些实例variables在范围之内,但是在使用之后使用声明在文本上出现的实例variables有时会受到限制。 特别是,如果以下所有情况都是正确的,则是编译时错误:

  • 在实例variables的使用之后,类或接口C中的实例variables的声明以文本forms出现;

  • 在C的实例variables初始值设定项或C的实例初始值设定项中,用法是简单的名称;

  • 使用不在作业的左侧;

  • C是封闭使用的最内层的类或接口。

在第二种情况下,使用不是一个简单的名字 – 你已经明确地得到了this名字。 这意味着它不符合上面引用的第二个列表中的第二个项目符号,所以没有错误。

如果您将其更改为:

 System.out.println("Test string is: " + testString); 

…那么它不会编译。

或者在相反的方向上,您可以将静态初始化程序块中的代码更改为:

 System.out.println("Test string is: " + StaticInitialisation.testString); 

奇怪的,但事情就是这样。

让我们看看这两个例子,我想这会让你清楚。

 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 

所以你可以说顺序是这样的。

  1. 静态variables将被创build,但不会被初始化。

  2. 静态初始化将按照给定的顺序执行。

  3. 非静态variables将被创build,但不会被初始化。
  4. 非静态初始化将按照给定的顺序执行。

按顺序我的意思是代码中的外观。

我想这个步骤回答你的两个不工作的例子StaticInitialisationInstanceInitialisation2

但万一你的第二个工作实例 InstanceInitialisation1通过使用this关键字,你实际上是帮助编译器忽略文本层次结构。 同样的事情发生在static情况下,当我在我的第一个例子InstanceAndSataticInit.testStringStatic InstanceAndSataticInit

简单的原因 – 分析和禁止所有前向引用太昂贵或不可能。 例如

 { print( getX(); ); // this.x print( that().x ); // this.x } int x; int getX(){ return x; } This that(){ return this; } 

规范决定禁止一些简单的案例指示常见的程序员错误。

另请参阅recursion初始值设定项在添加“this”时起作用?

在这里,我们必须了解的是,在第二个代码片段中您正在使用block和this关键字

  1. 如果创build了对象,则该块将执行。
  2. 这意味着对象是在堆区中创build的。
  3. 您正在外部使用此关键字来获取实例variables的值。
  4. 这里用默认值创build的对象将作为值返回。
  5. 不用使用这个关键字,你也不能编译第二个片段。