我读过一些文章和书籍,它们都说应该尽可能避免使用String s = new String("...");
。我明白为什么要这样做,但是是否有使用String(String)
构造函数的情况呢?我认为没有,也没有看到任何证据表明有用处,但我想知道SO社区是否有人知道。
我读过一些文章和书籍,它们都说应该尽可能避免使用String s = new String("...");
。我明白为什么要这样做,但是是否有使用String(String)
构造函数的情况呢?我认为没有,也没有看到任何证据表明有用处,但我想知道SO社区是否有人知道。
这是一篇好文章: String构造函数被认为是无用的,结果证明仍然有用!
It turns out that this constructor can actually be useful in at least one circumstance. If you've ever peeked at the String source code, you'll have seen that it doesn't just have fields for the char array value and the count of characters, but also for the offset to the beginning of the String. This is so that Strings can share the char array value with other Strings, usually results from calling one of the substring() methods. Java was famously chastised for this in jwz' Java rant from years back:
The only reason for this overhead is so that String.substring() can return strings which share the same value array. Doing this at the cost of adding 8 bytes to each and every String object is not a net savings...
Byte savings aside, if you have some code like this:
// imagine a multi-megabyte string here String s = "0123456789012345678901234567890123456789"; String s2 = s.substring(0, 1); s = null;
You'll now have a String s2 which, although it seems to be a one-character string, holds a reference to the gigantic char array created in the String s. This means the array won't be garbage collected, even though we've explicitly nulled out the String s!
The fix for this is to use our previously mentioned "useless" String constructor like this:
String s2 = new String(s.substring(0, 1));
It's not well-known that this constructor actually copies that old contents to a new array if the old array is larger than the count of characters in the string. This means the old String contents will be garbage collected as intended. Happy happy joy joy.
最后,Kat Marsen 提出以下几点:
首先,字符串常量永远不会被垃圾回收。其次,字符串常量会被intern'd,这意味着它们在整个VM中是共享的。这可以节省内存。但这并不总是你想要的。
String上的复制构造函数允许您从字符串字面值创建一个私有的String实例。这对于构建有意义的互斥对象(用于同步)非常有价值。
如果我没记错的话,唯一“有用”的情况是原始字符串是更大的后备数组的一部分。(例如作为子字符串创建)。如果你只想保留小的子字符串并回收大的缓冲区,那么创建一个新的字符串可能是有意义的。
仅仅是为了扩展Douglas的答案*,我可以给出一个明确的例子,说明我在哪里使用过它。考虑读取一个包含成千上万行单词的字典文件,每一行只有一个单词。最简单的读取方法是使用BufferedReader.readLine()
。
不幸的是,readLine()
默认会分配一个80个字符的缓冲区作为预期行长。这意味着通常它可以避免无意义的复制——浪费一点内存通常也不太糟糕。然而,如果你正在为应用程序的持续时间加载短单词的字典,你最终会有大量的内存永久性地被浪费掉。我的“小应用程序”消耗了比它应该消耗的更多的内存。
解决方案是改变这个:
String word = reader.readLine();
转换成这样:
String word = new String(reader.readLine());
当然要带上注释!
*
我不记得我是否与道格拉斯一起工作时遇到了这个问题,或者他只是偶然回答了这个特定的问题。
构造函数在一般情况下很多余,不建议使用。带有字符串参数的String构造函数很少使用,除非要创建现有字符串变量的独立副本。基本上只用它来“澄清”你的代码。没有更少。
来自javadoc
/**
* Initializes a newly created {@code String} object so that it represents
* the same sequence of characters as the argument; in other words, the
* newly created string is a copy of the argument string. Unless an
* explicit copy of {@code original} is needed, use of this constructor is
* unnecessary since Strings are immutable.
*
* @param original
* A {@code String}
*/
public String(String original){.....}
如果你不想给原始字符串一个句柄,那么可以使用它来获取原始字符串的副本。但是,由于字符串是不可变的,所以我不认为存在使用此功能的情况;)
String.substring()
的实现已更改以避免此问题,因此此内容已不再相关。 - Jesper