请参阅以下代码.
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行我错了也请更正..
正如Pete Kirkham所指出的,这是具体的实施.我的回答仅适用于Sun JRE,并且仅在Java 7更新6之前.
你正常的substring
调用只是创建一个新的字符串,引用与原始字符串相同的字符数组.这就是第5行也发生的事情.新字符串对象引用恰好分配给变量的事实不会改变方法的行为.
为了清楚起见,你说在第2行,新字符串仍将指向"星期一" - 字符串中的字符串数组引用将与用于"星期一"的字符串数组相同.但"星期一"本身就是一个字符串,而不是字符数组.换句话说,通过时间线2完成(并忽略GC),有两个字符串对象,两者都引用相同的char数组.一个计数为6,另一个计数为3; 两者的偏移量均为0.
你在第4行使用"字符串池"是错误的 - 那里没有池.但是,它与其他线路不同.当您调用String(String)
构造函数时,新字符串会获取原始字符数据的副本,因此它是完全独立的.如果您只需要一个包含非常大的原始字符串的一小部分的字符串,这将非常有用; 当你抓住小部分的副本时,它允许原始的大字符数组被垃圾收集(假设没有别的东西需要它).根据我自己的经验,这方面的一个很好的例子就是从一条线上读取线条.默认情况下,BufferedLineReader
将使用80个字符的缓冲区读取行,因此返回的每个字符串将使用至少80个字符的char数组.如果您正在阅读许多非常短的行(单个单词),那么只需通过使用奇怪的内容就可以减少内存消耗
line = new String(line);
可能非常重要.
这有帮助吗?
我知道第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()
方法.这将从池中检索字符串(如果已经实现了该值的字符串),或者创建新字符串并将其放入池中.请注意,池化的字符串具有不同的(再次依赖于实现)垃圾收集行为.