Java - 奇怪的静态字符串行为 - 新字符串(“xxx”) vs “xxx”

2022-09-03 04:39:50
public class Test {

    private static final String str1 = new String("en");
    private static Test instance = initInstance();

    private static final String str2 = new String("en");
    private static final String str3 = "en";

    private Test() {
    }

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

    private static Test initInstance() {
        instance = new Test();
        System.out.println(str1 + ',' + str2 + ',' + str3);
        return instance;
    }
}

从理论上讲,静态无处不在,它应该导致“en,en,en”。

结果: “en,null,en”

预期:“en,null,null”(因为我发现静态顺序实际上很重要)

有人能解释一下吗?“en”和new String(“en”)有什么不同?


答案 1

是的。在调用该方法时,str2 尚未初始化(字段按声明顺序初始化),而 str3 是编译时常量。

编译时常量由编译器在类文件中内联。new String(“..”) 不是常量,因为它使用构造函数。

字符串常量由字符串文本“”定义,它们被放置在 jvm 实例的字符串池中,以便重用它们。与此相反,使用新的 String(..) 会创建一个新实例,因此应避免使用。


答案 2

发生这种情况是因为首先初始化使用常量初始化的类变量,然后才完成更复杂的初始值设定项(如使用的表达式)(然后按源顺序完成)。参见 JLS §8.3.2.1new String()

8.3.2.1. 类变量的初始值设定项

[...]

在运行时,首先初始化使用常量表达式 (§15.28) 初始化的字段 (§12.4.2)。这也适用于接口中的此类字段 (§9.3.1)。这些字段是“常量”,即使狡猾的程序(§13.4.9)也永远不会观察到它们具有默认的初始值(§4.12.5)。staticfinal