静态和最终限定符的奇怪Java行为[重复]

问题描述 投票:76回答:4

这个问题在这里已有答案:

在我们的团队中,我们发现了一些奇怪的行为,我们使用了staticfinal限定符。这是我们的测试类:

public class Test {

    public static final Test me = new Test();
    public static final Integer I = 4;
    public static final String S = "abc";

    public Test() {
        System.out.println(I);
        System.out.println(S);
    }

    public static Test getInstance() { return me; }

    public static void main(String[] args) {
        Test.getInstance();
    }
} 

当我们运行main方法时,我们得到一个结果:

null
abc

我会理解它是否两次都写了null值,因为静态类成员的代码是从上到下执行的。

任何人都可以解释为什么会发生这种行为?

java final
4个回答
109
投票

以下是运行程序时采取的步骤:

  1. 在运行main之前,必须通过按外观顺序运行静态初始化程序来初始化Test类。
  2. 要初始化me字段,请开始执行new Test()
  3. 打印I的值。由于字段类型是Integer,看起来像编译时常量4变成计算值(Integer.valueOf(4))。该字段的初始化程序尚未运行,打印初始值null
  4. 打印S的值。由于它是使用编译时常量初始化的,因此将该值烘焙到引用站点中,打印abc
  5. new Test()完成,现在I的初始化程序执行。

课程:如果您依赖于急切初始化的静态单例,请将单例声明放在最后一个静态字段声明中,或者使用在所有其他静态声明之后发生的静态初始化程序块。这将使类完全初始化为单例的构造代码。


71
投票

S是一个编译时常量,遵循JLS 15.28的规则。因此,代码中任何出现的S都将替换为编译时已知的值。

如果你将I的类型更改为int,你也会看到同样的情况。


21
投票

由于Integer数据类型,您有奇怪的行为。关于JLS 12.4.2静态字段按您编写的顺序初始化,但首先初始化编译时常量。

如果您不使用包装类型Integer但使用int类型,则可以获得所需的行为。


14
投票

你的Test编译成:

public class Test {

    public static final Test me;
    public static final Integer I;
    public static final String S = "abc";

    static {
        me = new Test();
        I = Integer.valueOf(4);
    }

    public Test() {
        System.out.println(I);
        System.out.println("abc");
    }

    public static Test getInstance() { return me; }

    public static void main(String[] args) {
        Test.getInstance();
    }
}

如您所见,Test的构造函数在I初始化之前被调用。这就是为什么它为"null"打印I。如果你要交换meI的声明顺序,你会得到预期的结果,因为在调用构造函数之前会初始化I。您也可以将I的类型从Integer更改为int

因为4需要自动装箱(即包装在Integer对象中),所以它不是编译时常量,而是静态初始化程序块的一部分。但是,如果类型是int,则数字4将是编译时常量,因此不需要显式初始化。因为"abc"是编译时常量,所以S的值按预期打印。

如果你要更换,

public static final String S = "abc";

用,

public static final String S = new String("abc");

然后你会注意到S的输出也是"null"。为什么会这样?出于同样的原因,I也输出"null"。这些具有文字,常量值(不需要自动装箱,如String)的字段在编译时会被"ConstantValue"属性归因,这意味着只需查看类的常量池就可以解析它们的值,而无需运行任何代码。

© www.soinside.com 2019 - 2024. All rights reserved.