请参阅以下代码。
String s = "Monday";
if(s.subString(0,3).equals("Mon"){}
String s2 = new String(s.subString(0,3));
String s3 = s.subString(0,3);
我知道第2行仍将指向“星期一”,并且有一个新的String对象,其偏移量和计数设置为0,3。
第4行将在字符串池中创建一个新的字符串“Mon”并指向它。
但不确定第5行是否会表现得像第2行或第4行。
如果第2行或第4行我错了,请更正..
答案 0 :(得分:9)
正如Pete Kirkham所指出的,这是具体的实施。我的答案仅适用于Sun JRE,并且仅在Java 7更新6之前。
正常的substring
调用只是创建一个引用与原始字符串相同的字符数组的新字符串。这就是第5行也发生的事情。新的字符串对象引用恰好分配给变量的事实不会改变方法的行为。
为了清楚起见,你说在第2行中,新字符串仍将指向“Monday” - 字符串中的char数组引用将与用于“Monday”的char数组相同。但“星期一”本身就是一个字符串,而不是字符数组。换句话说,通过时间线2完成(并忽略GC),有两个字符串对象,两者都引用相同的char数组。一个计数为6,另一个计数为3;两者都有0的偏移量。
你对使用“字符串池”的第4行错了 - 那里没有汇集。但是,它与其他线路不同。当您调用String(String)
构造函数时,新字符串将获取原始字符数据的副本,因此它是完全独立的。如果您只需要一个包含非常大的原始字符串的一小部分的字符串,这将非常有用;当你抓住小部分的副本时,它允许原始的大字符数组被垃圾收集(假设没有别的东西需要它)。根据我自己的经验,这方面的一个很好的例子就是从一条线上读取线条。默认情况下,BufferedLineReader
将使用80个字符的缓冲区读取行,因此返回的每个字符串将使用至少80个字符的char数组。如果您正在阅读许多非常短的行(单个单词),那么仅通过使用奇怪的内容来消耗内存的差异
line = new String(line);
非常重要。
这有帮助吗?
答案 1 :(得分:7)
我知道第2行仍将指向“星期一”,并且有一个新的String对象,其偏移量和计数设置为0,3。
目前Sun JRE的实施情况也是如此。我似乎记得过去的Sun实现并不是这样,并且JVM的其他实现也不是这样。不要依赖未指定的行为。 GNU类路径可能会复制数组(我不记得手头用什么比率来决定何时复制,但是如果副本是原始的一小部分就会复制,这会将一个很好的O(N)算法转换为O(N ^ 2))。
第4行将在字符串池中创建一个新的字符串“Mon”并指向它。
不,它在堆中创建一个新的字符串对象,受到与任何其他对象相同的垃圾收集规则的约束。它是否共享相同的底层字符数组是依赖于实现的。不要依赖未指定的行为。
String(String)
构造函数说:
初始化一个新创建的String对象,使其表示与参数相同的字符序列;换句话说,新创建的字符串是参数字符串的副本。
String(char[])
构造函数说:
分配一个新的String,使其表示当前包含在字符数组参数中的字符序列。复制字符数组的内容;后续修改字符数组不会影响新创建的字符串。
遵循良好的OO原则,String
的任何方法实际上都不需要使用字符数组来实现,因此String
的规范的任何部分都不需要对字符数组进行操作。将数组作为输入的那些操作指定将数组的内容复制到String中使用的任何内部存储。字符串可以在内部使用UTF-8或LZ压缩,并符合API。
但是,如果您的JVM没有进行小比例子字符串优化,那么当您使用new String(String)
时,它有可能只复制相关部分,所以这是一个尝试它的情况看它是否改善了内存使用。并非所有影响Java运行时的东西都是由Java定义的。
要获取字符串池中equal
到字符串的字符串,请使用intern()
方法。这将从池中检索字符串(如果已经实现了该值的字符串),或者创建新字符串并将其放入池中。请注意,池化的字符串具有不同的(再次依赖于实现的)垃圾收集行为。
答案 2 :(得分:3)
注意:从Sun / Oracle Java中的Java 7更新6开始,String.substring创建的String不再共享父级的char数组。我们认为这种优化很少有用,并且不能证明offset
和count
字段的成本和复杂性。
一些链接:
答案 3 :(得分:1)
第5行----> s3 =周一。
答案 4 :(得分:0)
在Sun的实现中,String对象具有private final char value[]
字段。通过调用substring()创建新String时,不会创建新的char数组,新实例将使用原始对象的value
。第2行和第5行就是这种情况,新的String对象将使用s的char数组。
如果字符串长度小于char数组value
的总长度,则构造函数String(String)会创建一个新的char数组。因此,第4行中创建的String将使用新的char数组。
你应该看一下构造函数public String(String original)的source code,它非常简单。
答案 5 :(得分:0)
“子字符串通过从原始字符串中提取一部分原始字符串来创建新对象”。
直到Java 1.7,子字符串都保留了原始字符数组的引用,这意味着即使是5个字符长的子字符串,也可以通过保留强引用来防止1GB字符数组被垃圾回收。
此问题已在Java 1.7中修复,在Java 1.7中不再引用原始字符数组,但是这种更改也使创建子字符串位的时间花费很大。先前它在O(1)的范围内,在Java 7的最坏情况下可能是O(n)。
答案 6 :(得分:-1)
阅读此http://java.sun.com/j2se/1.4.2/docs/api/java/lang/String.html
“返回一个新字符串......”