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

Why do two programs have forward referencing errors while the third does not?

以下内容无法编译,给出 'illegal forward reference' 消息:

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();
    }
}

但是下面的不编译,给出一个'illegal forward reference'信息:

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 部分:

Use of class variables whose declarations appear textually after the use is sometimes restricted, even though these class variables are in scope (§6.3). Specifically, it is a compile-time error if all of the following are true:

  • The declaration of a class variable in a class or interface C appears textually after a use of the class variable;

  • The use is a simple name in either a class variable initializer of C or a static initializer of C;

  • The use is not on the left hand side of an assignment;

  • C is the innermost class or interface enclosing the use.

Use of instance variables whose declarations appear textually after the use is sometimes restricted, even though these instance variables are in scope. Specifically, it is a compile-time error if all of the following are true:

  • The declaration of an instance variable in a class or interface C appears textually after a use of the instance variable;

  • The use is a simple name in either an instance variable initializer of C or an instance initializer of C;

  • The use is not on the left hand side of an assignment;

  • C is the innermost class or interface enclosing the use.

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

如果将其更改为:

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

...那么它不会编译。

或者反之,可以将静态初始化块中的代码改为:

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

奇怪,但事情就是这样。

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

  1. 如果创建对象,则执行该块。
  2. 也就是说对象是在堆区创建的
  3. 您正在外部使用这个关键字来获取实例变量的值。
  4. 此处使用默认值创建的对象将 return 作为值。
  5. 如果不使用此关键字,您也无法编译第二个代码段。

让我们看看这两个例子,我想这会让你明白。

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. 静态变量将被创建但不会被初始化。

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

  3. 将创建非静态变量但不会对其进行初始化。
  4. 非静态初始化将按照给定的顺序执行。

我指的是在代码中出现的顺序。

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

但是如果你的 第二个工作示例 InstanceInitialisation1 通过使用 this 关键词你实际上是在帮助编译器忽略文本层次结构。当我在第一个示例 InstanceAndSataticInit

中调用 InstanceAndSataticInit.testStringStatic 时,在 static 的情况下也会发生同样的事情

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

{
    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"?