确定字符串是否为编译时常量

时间:2019-07-10 15:09:53

标签: jvm compile-time-constant string-interning

给出对任何String的引用,是否可以以编程方式确定这是否是对编译时间常数的引用?
或者,如果不是,那么是否不做s.intern() == s就将其存储在内部池中?

isConst("foo")                       -> true
isConst("foo" + "bar")               -> true   // 2 literals, 1 compile time string
isConst(SomeClass.SOME_CONST_STRING) -> true
isConst(readFromFile())              -> false
isConst(readFromFile().intern())     -> false  // true would be acceptable too

(以下注释内容:最初询问文字的问题)

1 个答案:

答案 0 :(得分:2)

为阐明最初的问题,每个字符串文字都是一个编译时常量,但并非每个编译时常量都必须源自一个字符串文字。

在运行时,为编译时常量构造或通过其他方式构造的String对象之间没有区别。为编译时常量构造的字符串会自动添加到池中,但是其他字符串也可以通过intern()手动添加到同一池中。由于字符串是延迟构造和添加的,因此甚至可以手动构造和添加字符串,以便稍后将具有相同值的编译时常量解析为该字符串。 This answer利用了这种可能性,以检测何时真正解决了编译时常量的String实例。

可以从该答案中衍生出一种方法来简单地检测字符串是否在池中:

public static boolean isInPool(String s) {
    return s == new String(s.toCharArray()).intern();
}

new String(s.toCharArray())构造一个具有相同内容的字符串,该字符串不在池中,并且如果intern()引用,则对其调用s必须解析为与s相同的引用。到池中的实例。否则,intern()可能会解析为另一个现有对象,或者添加我们的字符串或新构造的字符串并返回对其的引用,具体取决于实现方式,但是在两种情况下,返回的引用都将不同于{{1} }。

请注意,此方法的副作用是,如果之前没有将字符串添加到池中,则该字符串至少会留在下一个垃圾回收周期(可能直到下一个完整的gc)为止,具体取决于池中的字符串。实施。

测试方法可能对调试或满足好奇心很有用,但是在生产代码中永远不要使用它。应用程序代码不应依赖于该属性,注释中提出的用例(在性能关键的代码中强制使用池字符串)并不是一个好主意。

除了测试本身是昂贵的并且抵消性能改进的目的这一点之外,池字符串比非池字符串更好的基本假设是有缺陷的。不在池中并不意味着应用程序每次调用性能关键代码时都会执行昂贵的重构。它可能只是将引用保存在变量中或使用s,这两种方法都比调用HashMap更有效。实际上,在某些情况下,即使是临时字符串也可能是最有效的解决方案。