字符串对象相对于字符串字面值的优势是什么?

7

我想知道在我的Java代码中什么情况下需要使用字符串对象。好的,我理解了字符串字面量和字符串对象之间的区别,但是我想知道既然Java赋予我们创建字符串对象的能力,肯定有一些原因,在某些情况下创建字符串对象会很有用。因此,我想知道在哪种情况下我们可以优先选择使用字符串对象而不是字符串字面量。


6
字符串字面值是一个字符串对象。 - krock
在某些情况下,这几乎相当于询问为什么要使用“int”变量而不是数字常量。 - pascal
1
这并不是一个微不足道的问题(int vs. 常量)。会导致魔法数字 vs. “不是全局参数,只需要用一次…” 等等。 - Konrad Garus
是的,我记得曾经看到一种语言或编译器据说实现了全局数字常量字典。在类似于 const int a = 1; *(int*)&a = 2; 这样的代码之后,1 就会全局地变成 2... - pascal
你能更具体一些吗?看起来大多数答案都集中在String s = new String("xx")上。这就是你所说的吗? - pascal
6个回答

6
在大多数情况下,您应该使用字符串字面量来避免创建不必要的对象。这实际上是《Effective Java》中的第5条:避免创建不必要的对象

Item 5: Avoid creating unnecessary objects

It is often appropriate to reuse a single object instead of creating a new functionally equivalent object each time it is needed. Reuse can be both faster and more stylish. An object can always be reused if it is immutable (Item 15). As an extreme example of what not to do, consider this statement:

String s = new String("stringette"); // DON'T DO THIS!

The statement creates a new String instance each time it is executed, and none of those object creations is necessary. The argument to the String constructor ("stringette") is itself a String instance, functionally identical to all of the objects created by the constructor. If this usage occurs in a loop or in a frequently invoked method, millions of String instances can be created needlessly. The improved version is simply the following:

String s = "stringette";

This version uses a single String instance, rather than creating a new one each time it is executed. Furthermore, it is guaranteed that the object will be reused by any other code running in the same virtual machine that happens to con- tain the same string literal [JLS, 3.10.5]

然而,有一种情况下你需要使用new String(String)构造函数:当你想要强制将子字符串复制到一个新的底层字符数组中时,就像这样:

String tiny = new String(huge.substring(0, 10));

这将允许原始的huge字符串中的大型底层char[]被垃圾回收器回收利用。


你能解释一下为什么 String tiny = new String(huge.substring(0, 10)); 可以被垃圾回收吗?另一方面,为什么 tiny = huge.substring(0, 10); 不可以? - Sujee
3
使用 tiny = huge.substring(0, 10)tiny 将使用与 huge 相同的 char[]。而使用 String tiny = new String(huge.substring(0, 10))tiny 将使用一个新的(较小的)char[],允许大的 char[] 被回收利用。有关详细信息,请查看源代码。 - Pascal Thivent
使用 new String(String) 有一个微妙之处,请参见 https://dev59.com/4nRC5IYBdhLWcg3wKtv2#390854。 - Lawrence Dol

4

如果你知道字符串是什么,就不要使用新的String对象。例如:

String str = new String("foo"); // don't do this

因此,您正在创建一个不必要的对象 - 一旦您从字面上创建了一个String对象,然后您创建另一个对象,以第一个对象作为构造函数参数。


2
String a = "ABC";
String b = new String("ABC");
String c = "ABC";

a == b // false
a == c // true

a.equals(b) // true
a.equals(c) // true

重点在于a和c指向同一个“ABC”对象(JVM的魔法)。使用“new String”每次都会创建一个新对象。在我看来,使用字符串对象是一个劣势,而不是一个优势。然而,正如另一位评论者所说,如果需要转换byte[]、char[]、StringBuffer等,字符串对象很有用。

2
字符串字面量被转换为字符串对象,正如其他人指出的那样,创建显式的字符串对象是不必要且性能低下的,因为它破坏了字符串池
然而,在一个情况下你需要显式地创建新的字符串:如果你只使用非常长的字符串的一小部分。 String.substring() 防止原始字符串被垃圾回收,所以当你写代码时可以节省内存。
String s = new String(veryLongString.substring(1,3));

替代

String s = veryLongString.substring(1,3);

使用 new String(String) 有一个微妙之处;请参见 https://dev59.com/4nRC5IYBdhLWcg3wKtv2#390854。 - Lawrence Dol

2
与您的问题相反,使用字符串对象与字符串字面值相比有一个缺点。
当您声明字符串字面值时,例如String s = "foo",编译器将检查堆上是否存在一个现有的“foo”对象,并将's'分配给已经存在的“foo”。
但是,如果您创建一个字符串对象,例如String s = new String("foo"),将在堆上创建一个全新的对象(即使已经存在“foo”)。由于字符串是不可变的,这是完全不必要的。
这里有一个很好的参考:http://www.javaranch.com/journal/200409/ScjpTipLine-StringsLiterally.html

0

字面字符串是在字符串池中创建的对象,如果它们具有相同的值,则引用同一个对象。

System.out.println("abc"=="abc"); // the output is true

与此同时,字符串对象是内存中的真实对象,如果它们具有相同的值,则不能保证它们引用同一个对象。

String a = new String("abc");
String b = new String("abc");
System.out.println(a==b); // the output is false

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