Java 为什么在使用类名为的静态变量时未显示非法的前向引用错误

Java 为什么在使用类名为的静态变量时未显示非法的前向引用错误,java,Java,在下面的代码中,当访问具有类名的静态变量时,它不会抛出前向引用错误,但在没有类名的情况下访问它会抛出前向引用错误 为什么使用类名访问时不会发生这种情况 class Test{ static { System.out.println(a); // shows error a = 99; // and this line too doesn't give error System.out.println(Test.a); // this l

在下面的代码中,当访问具有类名的静态变量时,它不会抛出前向引用错误,但在没有类名的情况下访问它会抛出前向引用错误

为什么使用类名访问时不会发生这种情况

class Test{
    static {
        System.out.println(a); // shows error
        a = 99; // and this line too doesn't give error  
        System.out.println(Test.a); // this line doesn't
    }
    static int a = 10;  
    static{
        System.out.println(a);
    }
}

类初始值设定项是在加载类时运行的,您无法确定何时会发生这种情况。在类初始值设定项中运行的代码应该只用于需要处理或“初始化”以使其他静态方法(或类本身)正常工作的事情

此外,您正在引用尚未声明的变量。在类初始值设定项(第一个静态块)中,您指定了
a=99
,但尚未声明a。如果您想要声明变量,然后在静态块中初始化它

没有理由对发布的代码类型使用类初始值设定项。这应该是一个静态方法

这里有一个例子

class Test{
    static int a = 10;

    static void doSomething(){
        System.out.println(a);
        a = 99;
        System.out.println(a);
    }
}

然后主要可以调用
Test.doSomething()

那么,静态块

static {
    System.out.println(a);
    a = 99;
    System.out.println(Test.a);
}
将在声明
a
之前执行


带有
Test.a
的行不会触发任何错误,因为编译器检查并找到在类
Test
中声明的静态变量
a
,正向引用的规则定义如下:

使用类变量,其声明以文本形式显示在 使用有时会受到限制,即使这些类变量在 范围(§6.3)。具体来说,如果所有 以下是事实:

  • 此时会出现类或接口C中的类变量声明 使用类变量后的文本形式

  • 在C的类变量初始值设定项中使用一个简单的名称 或C的静态初始值设定项

  • 使用不在作业的左侧

  • C是包含该用法的最内层类或接口

因此,基本上您的第一个
Sysout()
,满足上述所有4个条件,因此这是一个编译时错误

在第二个
Sysout()
中,您使用的是
a
的限定名称,而不是简单名称,根据上述规则,这是允许的

现在,这样做的原因是,当您访问
Test.a
时,编译器确保
Test
类已加载,并且所有
静态
字段都已初始化,因此它可以访问
a
字段。但是当在简单名称上访问
a
时,编译器不确定
a
的初始值设定项是否已经运行,因为它可能仍在加载类的过程中

考虑以下类的加载过程:

  • 加载类时,将为其中声明的所有
    静态
    变量分配内存。此时,变量
    a
    已分配内存(声明已完成)
  • 然后,所有的
    静态
    初始值设定项都按照出现的顺序运行。
    • 第一个语句是
      Sysout(a)<代码>a
      尚未初始化,因此您无法访问它。(错误)
    • 第二条语句是
      a=99
      。这里实际上是初始化变量
      a
      。很好
    • 第三个是
      Sysout(Test.a)
      ——上面已经给出了这方面的理由。编译器知道
      测试已加载
    • 然后执行
      static int a=10
      。它将
      a
      重新初始化为
      10
      。记住,声明部分已经在第一步中处理好了
首先,让我们看看非法转发引用应该怎么说

使用类变量,其声明以文本形式显示在 使用有时会受到限制,即使这些类变量在 范围(§6.3)。具体来说,如果所有 以下是事实:

此时会出现类或接口C中的类变量声明 使用类变量后的文本形式

在C的类变量初始值设定项中使用一个简单的名称 或C的静态初始值设定项

使用不在作业的左侧

C是包含该用法的最内层类或接口

使用实例变量,其声明以文本形式显示在 即使这些实例变量 都在范围之内。具体来说,如果所有 以下是事实:

类或接口C中实例变量的声明 在使用实例变量后以文本形式显示

在实例变量初始值设定项或 C或C的实例初始值设定项

使用不在作业的左侧

C是包含该用法的最内层类或接口

它为每个静态变量和实例变量定义了什么是非法的正向引用。然而,两者的定义似乎是相同的。既然你的问题是关于静态的,我们只会深入探讨这个问题

1) 请参阅,对于静态变量,有
声明
初始化

 static int a; //only declaration
 static int b = 10; //both declaration and initialization
2) 如果类变量的声明在使用后以文本形式出现,则有时会受到限制,即使这些类变量在范围内

它向我们解释了什么?它说,在某些情况下,我们可以使用静态变量,即使我们在事后声明它们。在某些情况下,这是不允许的。那么,有哪些情况是不允许这样做的

如果以下4个条件同时为真。否则你可以自由使用它
a) The declaration of a class variable in a class or interface C appears textually after a use of the class variable;

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

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

d) C is the innermost class or interface enclosing the use.
class Test {

    static {
        System.out.println(a); // True + True + True +True ; Illegal forward reference
        a = 99; // True +  True + False +True ; No illegal forward reference
        System.out.println(Test.a); // True + False + True + True No illegal forward reference
    }

    static int a = 10;

    static {
        System.out.println(a);
    }
}
class Test {

    static {
        System.out.println(Test.a);  // prints 0
        a = 99; // a get 99
        System.out.println(Test.a); // prints 99
    }

    static int a = 10;

    static {
        System.out.println(a); // prints 10
    }

    public static void main(String[] args) {

    }
}