使用相同的字符串字面值而不是final变量有什么好处?

36

我遇到了一个类,其中包括多次使用字符串字面量"foo"。

我想知道的是,相比于声明字符串为final并将所有字面量替换为该final变量,使用这种方法的好处和影响(在对象创建、内存使用和速度方面)是什么?

例如(虽然显然不是真实的单词用法):

private static final String FINAL_STRING = "foo";

public void stringPrinter(){
    for(int i=0;i<10;i++){
        System.out.println(FINAL_STRING);
    }
}

比较:

public void stringPrinter(){
    for(int i=0;i<10;i++){
        System.out.println("foo");
    }
}
哪种方式更好,为什么(假设字符串值将保持不变)?
上面(第二个)示例会创建10个字符串对象吗?还是JVM会意识到实际只使用了一个字面量,并创建一个单一引用。如果是这样的话,将字符串声明为final是否有任何优势(就像第一个示例中)?
如果解释代码确实用单一引用替换字符串字面量,那么如果相同的字面量出现在多个地方,这种情况是否仍然适用:
public void stringPrinter(){
    for(int i=0;i<5;i++){
        System.out.println("foo"); // first occurence
        System.out.println("foo"); // second occurence
    }
}

我不会认为之前的开发人员选择重复字面量而不使用常量,这只是懒惰。 - matt b
奇怪的是,他们已经声明了常量但却没有使用它。这就是为什么我对可能的原因感到感兴趣。但是同意,我认为这只是懒惰。 - Michael
6个回答

40

它们将完全相同。在两种情况下,该文字字面值都被联接(任何编译时常量表达式的字符串结果与所有其他常量/文字字面值共享同一实例),一个聪明的编译器+运行时应该没有问题将两者缩减为最优化示例。

优势更多地体现在可维护性上。如果要更改文字字面值,则只需使用常量更改一个出现位置,但如果它们被内联包含,则需要搜索和更改每个实例。


24

来自JLS
类型为String的编译时常量总是“internalized”,以共享唯一实例,使用方法String.intern。

因此,不会有多个字符串对象。

正如马克所指出的,这严格是可维护性问题而非性能问题。


11

这种优势不在于性能,而在于可维护性和可靠性。

让我举一个最近遇到的真实例子。一位程序员创建了一个函数,它接受一个字符串参数,该参数标识了交易的类型。然后,在程序中他使用字符串比较来判断该类型。像这样:

if (type.equals("stock"))
{ ... do whatever ... }
然后他调用了这个函数,将值"Stock"传递给它。
你注意到大小写的区别了吗?原始程序员也没有注意到。这被证明是一个相当微妙的bug,因为即使查看两个列表,大小写的差异也没有引起我的注意。
如果相反,他声明了一个final static,比如说:
final static String stock="stock";

如果第一次尝试传入"Stock"而不是"stock",他会收到一个编译时错误消息。

在这个例子中更好的方法是使用枚举,但是假设他必须把字符串写入输出文件或其他地方,因此必须使用字符串。

final静态变量至少有以下x个优点:

(1) 如果你拼错了它,你会得到一个编译时错误,而不是可能难以察觉的运行时错误。

(2) 静态变量可以给一个值分配一个有意义的名称。哪个更易理解:

if (employeeType.equals("R")) ...
或者
if (employeeType.equals(EmployeeType.RETIRED)) ...

(3)当存在多个相关值时,你可以在程序顶部放置一组final statics,从而告诉未来的读者所有可能的值。我看过很多函数将一个值与两个或三个字面值进行比较,这让我感到困惑:是否存在其他可能的值,还是这就是全部?(更好的方式通常是使用枚举,但那又是另外一回事了。)


3
所有字符串文字都保存在一个字符串缓存中(跨越所有类)。
使用常量可以使代码更清晰,为字符串提供一些上下文,并使代码更易于维护,特别是如果相同的字符串出现在多个地方。

2

这些字符串字面量是内部化的,因此在循环中不会创建新的String对象。然而,尽管如此,使用相同的字面量两次仍可能是代码异味的迹象;但并非涉及速度或内存使用方面。


1
在你提供的情况下,我认为将其声明为FINAL_STRING的最大原因是确保它停留在一个集中的位置。该字符串常量只有一个实例,但第一个示例更容易维护。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接