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

时间:2015-03-19 19:50:07

标签: 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 line doesn't
    }
    static int a = 10;  
    static{
        System.out.println(a);
    }
}

4 个答案:

答案 0 :(得分:6)

前向引用规则在JLS §8.3.3

中定义
  

使用类声明变量,其声明以文本形式出现在   有时限制使用,即使这些类变量在   范围(§6.3)。具体来说,如果所有的话都是编译时错误   以下是真实的:

     
      
  • 出现类或接口C中的类变量声明   使用类变量后的文本;

  •   
  • 在C的类变量初始值设定项中,use是一个简单的名称   或C的静态初始化程序;

  •   
  • 使用不在作业的左侧;

  •   
  • C是封闭使用的最里面的类或接口。

  •   

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

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

现在,原因是,当您访问Test.a时,编译器确保已加载Test类并且已初始化所有static字段,因此它可以访问字段a。但是在简单名称上访问a时,编译器不确定a的初始化程序是否已经运行,因为它可能仍在处理加载类。

考虑以下加载类的过程:

  • 加载类时,将为其中声明的所有static变量分配内存。此时,变量a已分配内存(声明已完成)
  • 然后所有static初始化程序按发生顺序运行。
    • 第一个陈述是Sysout(a);a尚未初始化,因此您无法访问它。 (误差)
    • 第二个陈述是a = 99。在这里,您实际上是在初始化变量a。非常好。
    • 第三个是Sysout(Test.a) - 对此的推理已在上面发布。编译器知道Test已经加载。
    • 然后执行static int a = 10。它会将a重新初始化为10。请记住,声明部分已经在第一步处理。

答案 1 :(得分:1)

首先,让我们看看JLS对非法前向引用的看法。

  

使用类变量 ,其声明以文本形式出现在   有时限制使用,即使这些类变量在   范围(§6.3)。具体来说,如果所有的话都是编译时错误   以下是真实的:

     

出现类或接口C中的类变量声明   使用类变量后的文本;

     

在C的类变量初始值设定项中,use是一个简单的名称   或者是C的静态初始化器;

     

使用不在作业的左侧;

     

C是封闭使用的最里面的类或接口。

     

使用实例变量 ,其声明以文本方式显示   即使这些实例变量,有时也会限制使用   在范围内。具体来说,如果所有的话都是编译时错误   以下是真实的:

     

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

     

在实例变量初始值设定项中使用是一个简单的名称   C或C的实例初始值设定项;

     

使用不在作业的左侧;

     

C是封闭使用的最里面的类或接口。

它定义了每个静态和实例变量的非法前向引用。但是,两者的定义似乎相同。由于你的问题是关于静态的,我们只会深入研究它。

1)对于静态变量,请参见declaration并且initialization

 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.

嗯,a)点很简单。它表示只有在你想要使用静态变量时才必须查看这些规则,然后才会知道它为什么要深入研究这个JLS。

b)点是,如果您使用简单的名称,例如boy [而不是MyClass.boy附加的类名,那么您might会输入非法的问题转发参考,否则你是我的朋友。但只是这种情况不符合非法前向引用的条件,否则您的代码中的a=99会立即给我们错误。要生成此错误,还有2个条件必须成立。如果以下2不符合条件,您可以像这样使用它。]

c)非常简单。你没有在任务的左手边使用?如果我们看a=99,不要! System.out.println(a) - 这甚至不是任务。因此,没有左手分配是真的。

d)也很简单。它只是告诉你他在定义中意味着哪个类/接口C.你的C =测试。

现在让我们重新审视您的代码。在每行代码中,我将评论True+False+True+false这样的含义,即对于每一行,a),b),c),d)将给予每一行。好的?

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) {

    }
}

答案 2 :(得分:0)

类初始值设定项在加载类时运行,您无法确定何时发生这种情况。在类初始化程序中运行的代码应仅用于需要处理或“初始化”其他静态方法(或类本身)才能正常工作的内容。

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

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

这是一个例子

class Test{
    static int a = 10;

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

然后在主要内容中,您可以拨打Test.doSomething();

答案 3 :(得分:0)

好吧,静态块

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

将在声明a之前执行。

带有Test.a的行不会触发任何错误,因为编译器会检查并找到在类a中声明的静态变量Test