我知道你是否
for (condition) {
String s = "hi there";
}
在所有迭代中只创建一个String
实例,而不像String s = new String("hi there");
那样将在每次迭代中创建一个新实例。
但是,阅读约书亚布洛赫的有效Java:第2章第5项(第20页),它指出:
此外,保证对象将是 在同一个虚拟机中运行的任何其他代码重复使用包含相同的字符串文字[JLS, 3.10.5]。
AFAIK没有说碰巧 相同的字符串文字,它说包含。
阅读[JLS, 3.10.5]找不到任何确切的参考资料,我有疑问。
给出这个片段:
String s1 = "hi ";
String s2 = "there";
String s3 = "hi there";
创建了多少个实例?
s1
和s2
(然后重新使用s3
和s1
参考创建s2
)答案 0 :(得分:17)
JLS不保证任何子字符串的重用。这里的“包含”只是意味着类在某处提到完全相同的字符串文字。 不在“substring of”意义中使用。
答案 1 :(得分:3)
每个类文件都包含该类中使用的所有字符串文字或其他常量的列表(嵌入在指令流中的小数字常量除外)。如果列表中的项目19是字符串文字"Freddy"
,并且局部变量Fred
的索引为6,则为Fred="Freddy";
生成的字节码可能为ldc 19
/ astore 6
。
当加载一个类时,系统将构建一个包含所有常量的表,并且 - 对于引用类型的表 - 构建由此标识的对象。如果不知道存在字符串文字的实例,系统将向实例表中添加一个实例并存储对该实例的引用。生成机器代码时,ldc 19
将被替换为加载适当引用的指令。
重要的是,当一个类中的任何代码运行时,已经为其中的所有字符串文字创建了对象,因此像Fred="Freddy";
这样的语句将仅存储对已存在的String
的引用。 {1}}对象包含Freddy
,而不是创建新的String
对象。
答案 2 :(得分:2)
如果s3
重用s1
和s2
个实例,则s3
不会在物理上表示为连续字符数组,而是复合String
} String
个对象。
现在想象一下,在这样一个字符串中访问单个字符的性能影响 - 基于索引的访问实际上涉及将索引值与第一个字符串的大小进行比较,然后计算将成为第二个字符串索引的偏移量等。
实际上,相反的情况可能有意义:只能为"hi there"
(s3
)分配一个基础字符序列,而s1
和s2
只能存储它们的长度和该字符串中第一个字符的地址。但是我认为,对于jvm而言,确定“可嵌入的”是一项复杂而昂贵的工作。候选人和成本将超过潜在的利益。