为啥两个程序有前向引用错误而第三个没有?
Posted
技术标签:
【中文标题】为啥两个程序有前向引用错误而第三个没有?【英文标题】:Why do two programs have forward referencing errors while the third does not?为什么两个程序有前向引用错误而第三个没有? 【发布时间】:2015-08-30 13:02:28 【问题描述】:以下内容无法编译,给出“非法前向引用”消息:
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 可以?
【问题讨论】:
第三个 sn-p 如果更改为testString1 = this.testString2;
将起作用
这是我第一次在 Java 类中看到不属于 ctor、静态 ctor 或方法的代码块。很想知道它是什么,它如何通过编译以及何时执行
@sharonbn:它们是实例初始化器或静态初始化器,分别在 JLS 的 8.6 和 8.7 节中描述。
【参考方案1】:
JLS 的 8.3.3 部分对此进行了介绍:
在使用后以文本形式出现的类变量的使用有时会受到限制,即使这些类变量在范围内(第 6.3 节)。具体来说,如果以下所有条件都为真,则为编译时错误:
类或接口 C 中的类变量声明在使用类变量后以文本形式出现;
使用是 C 的类变量初始化器或 C 的静态初始化器中的简单名称;
用法不在作业的左侧;
C 是包含使用的最内层类或接口。
使用在使用后以文本形式出现的实例变量的使用有时会受到限制,即使这些实例变量在范围内。具体来说,如果以下所有条件都为真,则为编译时错误:
类或接口 C 中实例变量的声明在使用实例变量之后以文本形式出现;
使用是 C 的实例变量初始化器或 C 的实例初始化器中的简单名称;
用法不在作业的左侧;
C 是包含使用的最内层类或接口。
在您的第二种情况下,使用 不是 一个简单的名称 - 您明确地得到了 this
。这意味着它不符合上面引用的第二个列表中的第二个项目符号,因此没有错误。
如果你把它改成:
System.out.println("Test string is: " + testString);
...那么它不会编译。
或者反方向,你可以把静态初始化块中的代码改成:
System.out.println("Test string is: " + StaticInitialisation.testString);
奇怪,但事情就是这样。
【讨论】:
我来这里是因为你的推文,你得到了我的 +1,但这是@bayou.io 链接到的not new。 @MarkHurd:是的,这对我来说只是新的 :)【参考方案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
关键字,您实际上是在帮助编译器忽略文本层次结构。在我的第一个示例 InstanceAndSataticInit
InstanceAndSataticInit.testStringStatic
时,static
也会发生同样的情况
【讨论】:
【参考方案3】:原因很简单 - 分析和禁止所有前向引用太昂贵或不可能。例如
print( getX(); ); // this.x
print( that().x ); // this.x
int x;
int getX() return x;
This that() return this;
规范决定禁止一些指示常见程序员错误的简单案例。
另见Recursive initializer works when I add "this"?
【讨论】:
【参考方案4】:这里我们要了解的是,在第二个代码 sn-p 中,您使用的是 block 和 this 关键字。
-
如果创建了对象,则执行该块。
这意味着对象是在堆区创建的。
您在外部使用 this 关键字来获取实例变量的值。
此处使用默认值创建的对象将作为值返回。
如果不使用此关键字,您也无法编译 2nd sn-p。
【讨论】:
以上是关于为啥两个程序有前向引用错误而第三个没有?的主要内容,如果未能解决你的问题,请参考以下文章