为什么两个程序有前向引用错误,而第三个程序没有?
以下不会编译,给出一个“非法前向引用”的消息:
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
所以你可以说顺序是这样的。
-
静态variables将被创build,但不会被初始化。
-
静态初始化将按照给定的顺序执行。
- 非静态variables将被创build,但不会被初始化。
- 非静态初始化将按照给定的顺序执行。
按顺序我的意思是代码中的外观。
我想这个步骤回答你的两个不工作的例子StaticInitialisation
和InstanceInitialisation2
但万一你的第二个工作实例 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关键字 。
- 如果创build了对象,则该块将执行。
- 这意味着对象是在堆区中创build的。
- 您正在外部使用此关键字来获取实例variables的值。
- 这里用默认值创build的对象将作为值返回。
- 不用使用这个关键字,你也不能编译第二个片段。