使用静态和最终限定符的奇怪Java行为

srn*_*jak 76 java final

在我们的团队中,我们发现了一些奇怪的行为,我们使用了两者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();
    }
} 
Run Code Online (Sandbox Code Playgroud)

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

null
abc
Run Code Online (Sandbox Code Playgroud)

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

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

Mar*_*nik 109

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

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

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


Jon*_*eet 71

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

如果更改的类型Iint,你会看到,同样的,太.


sie*_*fer 21

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

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


Mar*_*ius 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();
    }
}
Run Code Online (Sandbox Code Playgroud)

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

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

如果你要更换,

public static final String S = "abc";
Run Code Online (Sandbox Code Playgroud)

用,

public static final String S = new String("abc");
Run Code Online (Sandbox Code Playgroud)

然后你会注意到输出S也是"null"如此.为什么会这样?出于同样的原因I也输出"null".像这些有文字,常数值(即字段需要自动装箱,像String)与归因"ConstantValue"编译时属性,这意味着它们的价值可以简单地通过查看类的常量池来解决,而无需运行任何代码.

  • @Tom这是一个没有区别的区别.`S`是`"abc"的另一个名字. (2认同)