采用字符串文字参数的 String 构造函数具有以下实现。
@IntrinsicCandidate
public String(String original) {
this.value = original.value;
this.coder = original.coder;
this.hash = original.hash;
}
据我了解,原始字符串文字(传递的参数)的值被分配给堆内存中新创建的对象。因此,我们有两个对象,一个在字符串文字池中,另一个在堆内存中。我假设由于字符串文字池不会被垃圾收集,因此创建的文字将保留在文字池中直到应用程序结束。这不是浪费内存资源吗?
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.
我相信源代码中的这条注释表明我们不要使用这个构造函数,除非需要显式副本,并且由于字符串是不可变的,所以这是不必要的。他们是否建议我们不要使用它,因为它很浪费,或者背后还有其他原因吗?如果他们确实建议不要使用它,因为它很浪费,为什么他们还要实现这个和无参数构造函数(那里的评论也说它是不必要的)?
难道他们不能只拥有采用字符数组、字节数组等的构造函数...而不是这两个构造函数吗?
Java 字符串创建和字符串池 上面的线程回答说,当您使用
new String("Argument")
时,确实会创建字符串文字,但我想知道的是为什么他们决定使用 String()
和 String("argument")
,如果它很浪费,而且他们自己建议不要这样做用吗?
这个构造函数的存在是有历史原因的。
在 Java 1.7 的早期版本和以前的版本中(也检查了 Java 1.5),
substring(int, int)
方法进行了优化,以便返回的字符串与原始字符串共享支持字符数组(调用内部构造函数的摘录):
public String substring(int beginIndex, int endIndex) { new String(offset + beginIndex, endIndex - beginIndex, value); }
String(int offset, int count, char value[]) { this.value = value; this.offset = offset; this.count = count; }
这样做的好处是以下操作不会复制字符:
String s1 = readSomeLongStringFromAFile("somefile.txt");
String s2 = s1.substring(2, s1.length()-2);
它的缺点是,在以下情况下,即使没有重用,支持字符数组也可能保留在内存中:
String getStartFromFile(String filename) {
String s1 = readSomeLongStringFromAFile("somefile.txt");
String s2 = s1.substring(0, 2);
return s2;
}
在这种情况下,使用复制构造函数是有益的,因为复制构造函数仅从后备数组中复制所需的字符:
String getStartFromFile(String filename) {
String s1 = readSomeLongStringFromAFile("somefile.txt");
String s2 = s1.substring(0, 2);
return new String(s2);
}
链接的源代码来自Java 1.7-b24,在最新版本的Java 1.7(1.7u351-ga)中,这个包私有构造函数已经消失了。
但是仍然有代码使用
String(String)
构造函数,并且 Java 试图尽可能避免破坏现有代码,这意味着 String(String)
复制构造函数在这里是为了迷惑人们。