字符串在我们的实际编程中是接触很多的,但是,一学到这就感觉头有点大。首先说下我遇到的问题吧。
1.Java中的String为什么是不可变的?
2.既然不可变,那么replace()函数算不算改变了String呢?
首先看个例子:
String s = "hello";
System.out.println(s);
s = "java";
System.out.println(s);
输出为:
hello
java
我相信一部分同学肯定跟我一样,看到这有点懵了,这不是改变了吗?为什么还说String是不可变的呢?
原因在于例子中的s只是一个String对象的引用,并不是对象本身,当执行s = "java"时,就创建了一个新的String对象"java",而原来的"hello"还存在于内存中。
图示如下:
是不是到这有点理解了?那来看我们的第一个问题,为什么String对象是不可变的?
源头还是从String源码上进行分析,源码如下(jdk版本1.8):‘
public final class String
implements java.io.Serializable, Comparable<String>, CharSequence {
//用于存储字符的字符数组,值不可更改
private final char value[];
//该字符串的hash code,默认值为0
private int hash; // Default to 0
我们可以看到String其实就是一个被封装的char数组,并且这个数组被final修饰,值不可更改。
继续看我们的第二个问题。同样的方法,看一下replace()函数的源码
public String replace(char oldChar, char newChar) {
if (oldChar != newChar)
{
int len = value.length;//value就是一个数组,这里得到string字符串的长度
int i = -1;
char[] val = value; /* avoid getfield opcode */
while (++i < len) //先确保字符串中有旧的字符
{
if (val[i] == oldChar)
{break;}
}
if (i < len)
{
char buf[] = new char[len];
for (int j = 0; j < i; j++)
{
buf[j] = val[j];
}
while (i < len) {
char c = val[i];
buf[i] = (c == oldChar) ? newChar : c;
i++;
}
return new String(buf, true); //!!!
}
}
return this;
}
注意三个!!!的位置,替换新的字符后,replace()内部返回一个新的String对象。String的其他方法也是一样的,都是在内部重新创建新的String对象,并且返回这个新的对象,原来的对象是不会改变的。
到这,上面提出的两个问题就大体上已经解决了。
下面再介绍下StringBuffer和StringBuilder两个类。参考:http://www.runoob.com/java/java-stringbuffer.html
当对字符串进行修改的时候,需要使用 StringBuffer 和 StringBuilder 类。
和 String 类不同的是,StringBuffer 和 StringBuilder 类的对象能够被多次的修改,并且不产生新的未使用对象。
StringBuilder 类在 Java 5 中被提出,它和 StringBuffer 之间的最大不同在于 StringBuilder 的方法不是线程安全的(不能同步访问)。
由于 StringBuilder 相较于 StringBuffer 有速度优势,所以多数情况下建议使用 StringBuilder 类。然而在应用程序要求线程安全的情况下,则必须使用 StringBuffer 类。
上面的话简单的说就是:
String长度大小不可变;StringBuffer和StringBuilder长度可变。
StringBuffer线程安全,而StringBuilder线程不安全,但是StringBuilder速度快。