最终字段初始化顺序

时间:2015-01-26 10:11:40

标签: java jls

这是一些在尚未初始化的类上调用静态方法A.f()的代码。 有人可以用JLS来解释这段代码的行为吗?

class A {
    final static Object b = new B();
    final static int S1 = 1;
    final static Integer S2 = 2;

    static void f() {
        System.out.println(S1);
        System.out.println(S2);
    }
}

class B {
    static {
        A.f();
    }
}

public class App
{
    public static void main( String[] args )
    {
        A.f();
    }
}

输出:

1
null
1
2

2 个答案:

答案 0 :(得分:29)

A.f()中的

App.main()会触发类A的初始化。

初始化所有常量变量。唯一的常量变量是S1,现在是1

然后,其他静态字段按文本顺序初始化。 b是第一个字段,它触发类B的初始化,后者又调用A.f()S2只是null,因为它尚未初始化。 b的初始化现已完成。最后但并非最不重要的是,S2已初始化为Integer对象2

S2不是常量变量,因为它不是基本类型int,而是引用类型IntegerS2 = 2;S2 = Integer.valueOf(2);的自动装箱速记。

  

如果字段声明中的声明符具有变量初始值设定项,则声明符具有声明变量的赋值(第15.26节)的语义。

     

[...]

     

请注意,作为常量变量的static字段(第4.12.4节)在其他static字段之前初始化(第12.4.2节)。这也适用于接口(第9.3.1节)。永远不会观察到这些字段具有默认的初始值(§4.12.5),即使是狡猾的程序也是如此。

8.3.2. Field Initialization

  

常量变量是基本类型或类型final的{​​{1}}变量,用常量表达式(第15.28节)初始化。变量是否是常量变量可能对类初始化(第12.4.1节),二进制兼容性(第13.1节,第13.4.9节)和明确赋值(第16节(定义赋值))有影响。

4.12.4. final Variables

  

常量表达式是表示基本类型值的表达式,或者是String不突然完成的值,仅使用以下内容组成:

     
      
  • 原始类型的文字和String
  • 类型的文字   
     

[...]

15.28. Constant Expressions

  

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

     

[...]

     
      
  1. 否则,记录当前线程正在进行C的LC对象初始化的事实,并释放Class

         

    然后,初始化C的LC字段,它们是常量变量(§4.12.4,§8.3.2,§9.3.1)。

  2.         

    [...]

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

12.4.2. Detailed Initialization Procedure

  

程序中的每个变量在使用其值之前必须具有值:

     
      
  • 每个类变量,实例变量或数组组件在创建时都使用默认值进行初始化(§15.9,§15.10.2):

         

    [...]

         
        
    • 对于所有参考类型(§4.3),默认值为static
    •   
  •   

4.12.5. Initial Values of Variables

答案 1 :(得分:2)

此问题似乎不属于JLS,但我们处理JVMS

Linking进程之前的resolution阶段,有Preparation次级,其中包括:

  

为类或接口创建静态字段并初始化   这些字段为默认值

以及更多:

  

静态字段的显式初始化程序作为其一部分执行   初始化(第5.5节),而不是准备

虽然initialization包括:

  

执行任何一个Java虚拟机指令new

初始化基元类型包括写入它们的初始值。对于引用类型字段,它们的默认值为null,因为在Resolution之前,子格jvm不“知道”哪个类与类的适当符号引用名相关联。