为什么我可以在初始化期间引用静态字段来写入,但不能读取?

时间:2016-11-30 13:49:19

标签: java

所以我只是编程了一下,当我遇到一些非常不需要的行为时,发现了一些非常奇怪的Java。让我们看一下(精简的)代码:

public class Main {

    static{
        test = "test2";           // this is fine!?
        System.out.println(test); //compilation error!
    }

    static String test = "test1"; // initialization line

    public static void main(String[] args) {
        System.out.println(test);
    }
}

此程序会为您提供编译时错误,说明您无法参考" test"在定义之前。但是我之前只做了一行。现在猜出当你注释掉错误行时输出是什么......

输出为:" test1",因此初始化会覆盖声明之前发生的分配!

此外,如果您没有初始化变量," test2"是输出。但是,如果您明确指定" null"作为初始化期间的值,输出将为" null"。

问题:

  1. 为什么我可以在声明变量之前访问要写入但不读取的变量?
  2. 为什么在第一次分配后完成初始化(这与术语"初始化"相矛盾)?
  3. 我一直认为用" null"显式初始化与省略初始化相同。这个例子显然是错误的。我只是想错了,还是这与规范相矛盾?
  4. 这真的是想要的Java行为吗?这是一个错误吗?

1 个答案:

答案 0 :(得分:3)

这是JLS中定义的预期行为。回答你的问题:

  
      
  1. 为什么我可以在声明变量之前访问要写入但不读取的变量?
  2.   

section 8.3.2.3

回答了这个问题
  

成员声明只有在成员是类或接口C的实例(分别是静态)字段并且满足以下所有条件时才需要以文本方式显示:

     
      
  • 用法发生在C的实例(分别是静态)变量初始值设定项或C的实例(分别是静态)初始值设定项中。

  •   
  • 用法不在作业的左侧。

  •   
  • 用法是通过一个简单的名称。

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

  •   
     

如果不满足上述四项要求中的任何一项,则为编译时错误。

因此,由于赋值的左侧是赋值 ,因此明确引用必须在声明之后的情况之一。

  
      
  1. 为什么在第一次分配后完成初始化(这与术语“初始化”相矛盾)?
  2.   

这由section 12.4.2回答。

  

接下来,按文本顺序执行类的类变量初始值设定项和静态初始值设定项,或接口的字段初始值设定项,就像它们是单个块一样。

所有初始化都发生在它们位于静态初始化块内,按照相对于每个其他初始化或静态块的顺序出现。该字段以其默认值null开头,在静态块中使用,然后进行初始化。

  
      
  1. 我一直认为用“null”显式初始化与省略初始化相同。对于这个例子来说,这显然是错误的...我只是想错了或者这与规范相矛盾吗?
  2.   

由于你在这里看到的原因,它们并不完全相同。如果在初始化发生之前修改静态块中的值,它将在静态初始化阶段被设置回null。

  
      
  1. 这真的是想要的Java行为吗?这是一个错误吗?
  2.   

是的,这是想要的行为。正如上面链接的JLS部分所示,这不仅仅是一个意想不到的事情。有关它的规则明确规定允许这样做。