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

2022-08-31 19:36:50

以下内容不会编译,给出“非法转发引用”消息:

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

为什么 StaticInitialization 和 InstanceInitialisation2 不能编译,而 InstanceInitialisation1 可以编译?


答案 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);

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


答案 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

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

  1. 将创建静态变量,但不会对其进行初始化。

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

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

通过序列,我的意思是代码中的外观。

我想这个步骤回答了你的两个不起作用的例子 StaticInitializationInstanceInitialisation2

但是,如果你的第二个工作示例实例初始化1通过使用关键字,你实际上是在帮助编译器忽略文本层次结构。当我在我的第一个示例中调用AdjectionsAndSataticInit时,也会发生同样的事情thisstaticInstanceAndSataticInit.testStringStatic


推荐