Java中的String,StringBuffer和StringBuilder
在了解这个问题的时候查了不少资料,最有帮助的是这个博文:http://swiftlet.net/archives/1694,看了一段时间,咀嚼了一段时间,写一个经过自己消化的博文,希望能帮到大家。
首先,String,StringBuffer,和StringBuilder都是对char[]的封装,而不同点在于String可以被认为是字符串常量,而StringBuffer,StringBuilder则是字符串变量。
String的成员变量如下:
1 private final char value[]; 2 private final int offset; 3 private final int count;
注意到char value[]是final的,代表着对象一旦被创建就不可改。我们平时常对String执行类似如下操作看起来更改字符串比较自由随意,但是其本身是不断创建新对象的过程:
String a="Hello World"; a=a+"!"; System.out.println(a); //result: Hello World!
所以,对String执行这种经常修改字符串的操作是效率低下的。
与String不同,StringBuffer和StringBuilder都继承于AbstractStringBuilder,所以我们从AbstractStringBuilder看起
其中成员变量如下
1 char value[]; 2 int count;
首先不是final的,其次这个类引入了很多对“容量”控制的方法,很像C++中的<Vector>
例如扩容:当添加字符的长度使得char value[]超出了其本身的最大大小,需要创建一个更大的char[]并将内容复制:
1 void expandCapacity(int minimumCapacity) 2 { 3 int newCapacity = (value.length + 1) * 2; 4 if (newCapacity < 0) 5 { 6 newCapacity = Integer.MAX_VALUE; 7 } else if (minimumCapacity > newCapacity) 8 { 9 newCapacity = minimumCapacity; 10 } 11 char newValue[] = new char[newCapacity]; 12 System.arraycopy(value, 0, newValue, 0, count); 13 value = newValue; 14 }
同样,char[]不可能所有容量都被使用,结束标识符同C一样是“\0”;
补长或者截断:
1 public void setLength(int newLength) 2 { 3 if (newLength < 0) 4 throw new StringIndexOutOfBoundsException(newLength); 5 if (newLength > value.length) 6 expandCapacity(newLength); 7 if (count < newLength) 8 { 9 for (; count < newLength; count++) 10 value[count] = '\0'; 11 } else 12 { 13 count = newLength; 14 } 15 }
内存复制的方法常用的是:
public static native void arraycopy(Object src, int srcPos, Object dest, int destPos, int length);
那么StringBuffer和StringBuilder有什么区别:先从StringBuffer和StringBuilder到String的转换看起:
1 public String(StringBuffer buffer) 2 { 3 synchronized(buffer) 4 { 5 this.value = Arrays.copyOf(buffer.getValue(), buffer.length()); } 6 } 7 public String(StringBuilder builder) 8 { 9 this.value = Arrays.copyOf(builder.getValue(), builder.length()); 10 }
可见,对于StringBuffer而言,处处要考虑其在多线程环境下的并发问题。也就是说,相比于StringBuilder而言,StringBuffer是线程安全的。
另外,关于String的intern方法,是将字符串常量放在一个由String类维护的常量池中。当调用intern 方法时,如果池中已经包含一个等于此String 对象的字符串(是否相等由 equals(Object)方法确定),则返回池中的字符串引用。否则,将此 String 对象添加到池中,并且返回此String 对象的引用。例如:对于任何两个字符串s和t,当且仅当s.equals(t)为true时,s.intern()==t.intern()才为true。