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

Java 为什么两个程序有前向引用错误,而第三个程序没有?,java,static,instance,forward,Java,Static,Instance,Forward,以下内容未编译,给出“非法转发引用”消息: class StaticInitialisation { static { System.out.println("Test string is: " + testString); } private static String testString; public static void main(String args[]) { new StaticInitialisat

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

class StaticInitialisation {

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

    private static String testString;

    public static void main(String args[]) {
        new StaticInitialisation();
    }
}
class InstanceInitialisation2 {

        private String testString1;

    {
        testString1 = testString2;
    }

    private String testString2;

    public static void main(String args[]) {
        new InstanceInitialisation2();
    }
}
但是,以下内容不适用:

class InstanceInitialisation1 {

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

    private String testString;

    public static void main(String args[]) {
        new InstanceInitialisation1();
    }
}
但以下内容未编译,给出“非法转发引用”消息:

class StaticInitialisation {

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

    private static String testString;

    public static void main(String args[]) {
        new StaticInitialisation();
    }
}
class InstanceInitialisation2 {

        private String testString1;

    {
        testString1 = testString2;
    }

    private String testString2;

    public static void main(String args[]) {
        new InstanceInitialisation2();
    }
}

为什么StaticInitialization和InstanceInitialization2不编译,而InstanceInitialization1编译?

这在JLS的一节中介绍:

有时,即使这些类变量在范围内(§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);

奇怪,但事情就是这样。

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

  • 如果创建了对象,则执行块
  • 这意味着对象是在堆区域中创建的
  • 您正在外部使用此关键字获取实例变量的值
  • 此处创建的对象具有将作为值返回的默认值
  • 如果不使用此关键字,您也无法编译第二个代码段

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

    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
    

    输出:

    Test string is (static init ): test
    Test string is (instance init): test
    
    所以你可以说序列是这样的

  • 将创建静态变量,但不会初始化

  • 静态初始化将根据给定的顺序执行

  • 将创建非静态变量,但不会初始化
  • 非静态初始化将根据给定的顺序执行
  • 我所说的顺序是指代码中的外观

    我想这两个步骤回答了您的两个不起作用的示例
    静态初始化
    实例初始化2


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

    原因很简单-分析和禁止所有正向引用太昂贵或不可能。e、 g

    {
        print( getX();  );    // this.x
        print( that().x );    // this.x
    }
    
    int x;
    int getX(){ return x; }
    
    This that(){ return this; }
    
    该规范决定禁止一些简单的案例,以表明程序员的常见错误


    另请参见

    如果将第三个代码段更改为
    testString1=this.testString2,则第三个代码段可以工作
    这是我第一次在Java类中看到一个代码块,它不是ctor、静态ctor或方法的一部分。我很想知道它是什么,它是怎么通过编译的,什么时候通过的executed@sharonbn:它们是实例初始值设定项或静态初始值设定项,分别在JLS的第8.6节和第8.7节中描述。我来这里是因为你的推特,你得到了我的+1,但这是@bayou.io链接。@MarkHurd:是的,这对我来说是新的:)