静态变量初始化的顺序,Java

时间:2012-11-25 19:17:34

标签: java static

  

可能重复:
  Java static class initialization
  in what order are static blocks and static variables in a class executed?

当我运行此代码时答案是1,我认为它将是2。 初始化的顺序和每个步骤中k的值是什么?

public class Test {

static {k = 2;}
static int k = 1;

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

编辑1:作为“k设置为默认值”的后续内容,为什么下一个代码无法编译?这是一个错误“在定义之前无法引用字段”。

public class Test {

static {System.out.println(k);}
static int k=1;

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

编辑2:对于一些我不知道的原因,当^而不是“k”的“Test.k”时,它会起作用。

感谢所有答案。这将是完美的:D

2 个答案:

答案 0 :(得分:8)

它们按照您编写的顺序执行。如果代码是:

public class Test {

    static int k = 1;
    static {k = 2;}

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

}

然后输出变为2。

初始化顺序为:..the class variable initializers and static initializers of the class..., in textual order, as though they were a single block.

值(对于你的代码)是:k = 0(默认值),然后它被设置为2,然后它被设置回1。

您可以通过运行以下代码来检查它是否实际设置为2:

private static class Test {

    static {
        System.out.println(Test.k);
        k = 2;
        System.out.println(Test.k);
        }
    static int k = 1;

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

答案 1 :(得分:4)

简短回答

当类的初始化开始时,k的初始值为0。

然后执行静态块(因为它在声明中的赋值之前),并且k将被分配2。

然后执行声明中的初始化程序,并为k分配1。

长解释

让我们使用this example,因为您的示例有点简单:

class TestInitOrder {
  static {
    System.out.println(TestInitOrder.stat1);
    System.out.println(TestInitOrder.stat2);
    System.out.println(TestInitOrder.str);
    System.out.println(TestInitOrder.str2);

    str = "something";

    System.out.println(TestInitOrder.str);
    System.out.println(TestInitOrder.str2);
    System.out.println(TestInitOrder.lazy);
    System.out.println(TestInitOrder.second);
  }

  private static final int stat1 = 10;
  static final String str2 = "sdfff";
  static String str = "crap";
  private static int stat2 = 19;
  static final Second second = new Second();
  static final int lazy;

  static {
    lazy = 20;
  }

  static {
    System.out.println(TestInitOrder.str2);
    System.out.println(TestInitOrder.stat2);
    System.out.println(TestInitOrder.str);
    System.out.println(TestInitOrder.lazy);
    System.out.println(TestInitOrder.second);
  }

  public static void main(String args[]) {
  }

}

class Second {
  public Second() {
    System.out.println(TestInitOrder.second);
  }
}

根据Java Language Specification,来自section 4.12.5

  

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

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

(规范中的以下行指定了所有类型的默认值,基本上是某种形式的0,例如00.0dnullfalse等等。)

因此,在初始化类之前(由于these reasons之一),变量将保持初始值。

根据detailed initialization procedure(这里仅引用有趣的步骤,并强调我的):

  

6。   [...]然后,初始化值 编译时 final类变量字段 常量表达式(§8.3.2.1,§9.3.1,§13.4.9,§15.28)。

     

[...]

     

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

让我们看一下第4步,其中包含4个final类变量:stat1str2secondlazy

由于10是常量表达式,因此"sdfff"str2,并且由于执行顺序,观察 {{1}的初始值是不可能的}和stat1。为了进行观察,你最早能做的就是在第9步。

second的情况表明,当右侧不是编译时常量表达式时,其初始值是可见的。

lazy的情况不同,因为赋值是在静态块中完成的,因此在步骤9中发生 - 因此可以观察其初始值。 (好吧,编译器会仔细检查lazy只分配一次)。


在使用编译时常量表达式初始化最终类变量之后,执行静态块和其余的初始化器。

从示例中可以看出,静态块和初始化是根据文本顺序进行的 - 使用str变量进行演示 - 首先将其打印为null,然后{{1然后something