使用相同的字符串文字而不是最终变量?

我遇到过一个包含字符串文字“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 } } 

它们将完全相同。 在两种情况下,文本都被实现(任何编译时常量表达式,导致该字符串与所有其他常量/文字共享相同的实例),智能编译器+运行时应该没有任何问题,将两者都减少到最优化的示例。

优点在于可维护性。 如果要更改文字,则只需要使用常量更改一个实例,但如果它们包含在内,则需要搜索并更改每个实例。

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

所以,不,只有一个字符串对象。

正如马克所说,这完全是可维护性而非性能的问题。

优点不在于性能,而在于可维护性和可靠性。

让我举一个最近才遇到的真实例子。 程序员创建了一个函数,该函数接受一个标识事务类型的String参数。 然后在程序中他做了字符串比较这种类型。 喜欢:

 if (type.equals("stock")) { ... do whatever ... } 

然后他调用了这个函数,将值传递给了“Stock”。

你注意到资本化的差异吗? 最初的程序员也没有。 事实certificate这是一个相当微妙的错误,因为即使查看两个列表,资本化的差异也没有给我带来影响。

相反,如果相反,他宣布了最终的静态

 final static String stock="stock"; 

然后他第一次尝试传入“Stock”而不是“stock”时,他会遇到编译时错误。

在这个例子中更好的做法是创建一个枚举,但我们假设他实际上必须将字符串写入输出文件或其他东西,所以它必须是一个字符串。

使用final statics至少可以获得x个优点:

(1)如果你拼写错误,你会得到一个编译时错误,而不是一个可能微妙的运行时错误。

(2)静态可以为值分配一个名称。 哪个更容易理解:

 if (employeeType.equals("R")) ... 

要么

 if (employeeType.equals(EmployeeType.RETIRED)) ... 

(3)当存在多个相关值时,您可以在程序的顶部放置一组最终静态,从而告知未来读者所有可能的值是什么。 当我看到一个函数将值与两个或三个文字进行比较时,我已经有很多次了。 这让我想知道:还有其他可能的价值观,或者是这样吗? (更好的是经常有一个枚举,但这是另一个故事。)

所有字符串文字都保存在字符串缓存中(这是跨所有类)

使用常量可以使代码更清晰,给字符串一些上下文,如果相同的字符串出现在多个位置,则使代码更容易维护esp。

这些字符串文字是内化的,因此循环中不会创建新的String对象。 但是,使用相同的文字两次仍然可以成为代码气味的标志; 但不是在速度或内存使用方面。

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