我遇到过一个包含多个字符串文字用法的类,“foo”。
我想知道的是,使用这种方法有什么好处和影响(在对象创建,内存使用和速度方面),而不是将String声明为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个String对象,或者JVM是否意识到实际只使用了一个文字,并创建了一个引用。如果是这样,将String声明为final是否有任何优势(如第一个例子中所示)?
如果解释的代码确实用单个引用替换字符串文字,那么如果相同的文字出现在多个地方,那么它仍然适用:
public void stringPrinter(){
for(int i=0;i<5;i++){
System.out.println("foo"); // first occurence
System.out.println("foo"); // second occurence
}
}
答案 0 :(得分:31)
他们将完全一样。在两种情况下,文本都是实例化的(任何编译时常量表达式导致该字符串与所有其他常量/文字共享相同的实例),智能编译器+运行时应该没有任何问题将两者都减少到最优化的示例。
优点在于可维护性。如果要更改文字,则只需要使用常量更改一个实例,但如果它们包含在内,则需要搜索并更改每个实例。
答案 1 :(得分:22)
From the JLS
String类型的编译时常量始终是“实例化”,以便使用String.intern方法共享唯一实例。
所以,不,只有一个字符串对象。
正如马克所说,这完全是可维护性而非性能的问题。
答案 2 :(得分:8)
优势不在于性能,而在于可维护性和可靠性。
让我举一个最近才遇到的真实例子。程序员创建了一个函数,该函数接受一个标识事务类型的String参数。然后在程序中他做了字符串比较这种类型。像:
if (type.equals("stock"))
{ ... do whatever ... }
然后他调用了这个函数,并将值传递给了“Stock”。
你注意到资本化的差异吗?最初的程序员也没有。事实证明这是一个相当微妙的错误,因为即使查看两个列表,资本化的差异也没有给我带来影响。
相反,如果他宣布了最终的静态,请说
final static String stock="stock";
然后,当他第一次尝试传入“Stock”而不是“stock”时,他会遇到编译错误。
在这个例子中,最好还是制作一个枚举,但我们假设他实际上必须将字符串写入输出文件或其他东西,所以它必须是一个字符串。
使用final statics至少可以获得x个优势:
(1)如果你拼写错误,你会得到一个编译时错误,而不是一个可能微妙的运行时错误。
(2)静态可以为值分配一个名称。哪个更容易理解:
if (employeeType.equals("R")) ...
或
if (employeeType.equals(EmployeeType.RETIRED)) ...
(3)当存在多个相关值时,您可以在程序的顶部放置一组最终静态,从而告知未来读者所有可能的值。当我看到一个函数将值与两个或三个文字进行比较时,我已经有很多次了。这让我想知道:还有其他可能的价值观,还是这样呢? (更好的是经常有一个枚举,但这是另一个故事。)
答案 3 :(得分:3)
所有字符串文字都保存在字符串缓存中(这适用于所有类)
使用常量可以使代码更清晰,给字符串一些上下文,如果相同的字符串出现在多个位置,则使代码更容易维护esp。
答案 4 :(得分:2)
这些字符串文字是内化的,因此循环中不会创建新的String对象。但是,使用相同的文字两次仍然可能是代码气味的标志;但不是在速度或内存使用方面。
答案 5 :(得分:1)
在您提供的案例中,我认为将其声明为FINAL_STRING
某处的最大原因是确保它保留在一个集中位置。只有该字符串的一个实例常量,但第一个例子更容易维护。