今天写程序时用到了String.replace(char oldChar, char newChar)方法。感觉很困惑,本来这个方法时新的字符代替旧的字符,可我发现使用了以后没有替换。这是为什么呢?经过思考如下:
先贴上代码:
public class Test {
public static void main(String[] args){
Test test = new Test();
String s = new String("hello");
test.notice(s);
System.out.println(s);
}
public static void notice(String str){
str = str.replace('h', 'x');
}
}
输出结果为:hello
我们会发现,字符串hello的‘h’没有被替换成‘x’。
原因如下:
1:String的参数传递传递的是值而不是引用,为什么一定要传值呢?
2:打开String类的源代码我们可以看到String类时用final修饰的,是不能被改变的。
3:而且String类的底层是由一个字符数组去实现的,数组的值实在初始化的时候就决定的,然而如果说java的String类型可以改变,那么用户可以随便 的访问数组,造成了安全隐患。
我们来分析为什么2中说String类型不可以被改变:
先来看这一段代码:
public class Test {
public static void main(String[] args){
String s = "x";
s = "k";
}
}
在String s = “x”这行加上断点调试的结果:
这回再看下一行的调试结果:
我们会发现,s都是同一个String对象,但是他们的id不相同。
这就是String的不可变性:深究原因我们会发现:java中的String只能重新创建,而不能改变。
就上面的例子来说,s开始时为x,这时s是一个对象,当s的结果被我们修改成k之后,并不是k覆盖了s,而是在内存中虚拟机又为我们创建可k这个对象,然后将s指向了k,然而。x这个字符串还是存在的,只是我们的对象s没有了指向他的指针罢了。
画一张内存图大家也许会更清楚:
再看看我们的replace这个函数,当我们用值传递时,字符串也会被改变,但是原来的字符串依然在内存中,只是原来的对象指向了新创建的字符串:可以看到replace函数的源码中有:
pu<span style="font-size:12px;">blic String replace(char oldChar, char newChar) {
if (oldChar != newChar) {
int len = count;
int i = -1;
char[] val = value; /* avoid getfield opcode */
int off = offset; /* avoid getfield opcode */
while (++i < len) {
if (val[off + i] == oldChar) {
break;
}
}
if (i < len) {
char buf[] = new char[len];
for (int j = 0 ; j < i ; j++) {
buf[j] = val[off+j];
}
while (i < len) {
char c = val[off + i];
buf[i] = (c == oldChar) ? newChar : c;
i++;
}
return new String(0, len, buf);
}
}
return this;
}</span>
我们可以看到他的返回语句为:new String(0,len,buf);所以,替换的新字符串为新创建的字符串,并且创建以后改变了原来对象的指针。