Java 为什么静态块不能访问在它之后定义的静态变量

Java 为什么静态块不能访问在它之后定义的静态变量,java,java-8,static,static-block,Java,Java 8,Static,Static Block,我已经检查过了,但仍然没有找到原因的答案 为什么静态块可以分配在它之后声明的静态变量,但不能访问它 class Parent { static { i = 2; // valid // can only assign new value to it instead of accessing it? // System.out.println(i); // invalid - compile-error

我已经检查过了,但仍然没有找到原因的答案

为什么静态块可以分配在它之后声明的静态变量,但不能访问它

   class Parent {
        static {
            i = 2; // valid
            // can only assign new value to it instead of accessing it?
//            System.out.println(i); // invalid - compile-error
        }
        static int i = 0;
        static {
            i = 3; // valid
        }
    }
这是因为:该值尚未初始化,所以我们只是显式禁止您使用它?或者有什么和安全有关的事我不知道

更新 这不是一个重复的问题,这是关于

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


这个问题是关于为什么我们有这个设计?出于什么目的?

静态字段根据它们在代码中出现的顺序进行初始化

所以,当你给i变量赋值时,你只需对编译器说:嘿,伙计,当你开始初始化这个变量时,把它的值设置为。。。。但是在初始化之前不能使用它,因为它根本不存在

更新:

正如James Gosling、Bill Joy、Guy Steele和Gilad Bracha在《Java语言规范》一书中所说:

这些限制旨在在编译时捕获循环或 否则,初始化格式不正确

考虑这一点:

static {
            i = 2;
            j = i + 5; //should it be 7 or 15?
}
static int i = 10;
static int j;
j变量应该是7还是15? 如果是7,那么我们已经初始化了两次i变量,这是不可能的,因为该字段是静态的。如果是15,那么i=2是什么;什么意思


这段代码模棱两可,所以Java规范不允许这样做。

进一步阅读后,我认为Pavel在这一点上并不十分准确,正如评论中所指出的那样

我们已经初始化了两次i变量,这是不可能的,因为该字段是静态的

正如报告所指出的那样

对于每个类或接口C,都有一个唯一的初始化锁LC。从C到LC的映射由Java虚拟机实现自行决定

我认为初始化两次对类初始值设定项本身来说是可以的,只要对调用的客户机来说只有一次

但帕维尔提供的演示仍然站在它的立场上,所以我基本上只是在这里重复使用它,但有不同的解释

static {
       i = 2;
       j = i + 5; 
       // no one knows whether "i" here initialized properly here
}
static int i = 10;
static int j;
但是,当您直接在j=MyClass.i+5中使用MyClass.i时,编译器就会知道这样就可以了,具体有四个条件

具体地说,如果以下所有情况都是错误的,则这是一个编译时错误 正确:

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

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

使用不在作业的左侧

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

在这方面已经有了详细的讨论

最后,我认为这将是为可预测的行为添加这些限制。另一个官方目的再次在报告中指出

这些限制旨在在编译时捕获循环或其他格式错误的初始化


这同样适用于非静态构件。您可以在实例初始值设定项块中为它们赋值,但不能在初始化之前使用它们

class Parent {
    {
        x = 3; // works fine
        // System.out.println(x); // gives compilation error.

    }
    int x = 0;

    public static void main(String[] args) {

    }
}

将出现@Lino提到的编译错误,因此您无法获得bytecode@Lino这段代码无法编译。没有要检查的字节码。不要在这里发表毫无意义的猜测。是的,这是因为它还没有初始化,这就是错误消息所说的,不是吗?可能重复@Hearen询问为什么要做出语言决定并不容易回答问题。这需要一位java开发人员非常深入的知识,除非Brian Goetz过来,否则我不确定这里是否有人能回答这个问题,并提供了一些解释。哇,非常感谢。我只是迷路了,不知道为什么这是不允许的。这个例子确实给了我线索。非常感谢你!Pavel“那么我们已经初始化了两次i变量,这是不可能的,因为该字段是静态的”啊,你为什么认为这是不可能的?您可以随意分配变量,除非它是最终变量。您可以轻松地编写静态int i=2,j=i+++5;静态{i+=3;}或更改j=i+5;在您自己的示例中,j=ClassName.i+5;在Java规范中是这样说的:在运行时,初始化器被计算,赋值在类初始化时执行一次。这里和这里关于引用变量的全名。在这种情况下,编译器确切地知道类已经初始化。这里讨论了这个问题: