Consider below code with three concatenation functions with three different types of parameters, String, StringBuffer and StringBuilder:
// Java program to demonstrate difference between String,
// StringBuilder and StringBuffer
class Geeksforgeeks
{
// Concatenates to String
public static void concat1(String s1)
{
s1 = s1 + "forgeeks";
}
// Concatenates to StringBuilder
public static void concat2(StringBuilder s2)
{
s2.append("forgeeks");
}
// Concatenates to StringBuffer
public static void concat3(StringBuffer s3)
{
s3.append("forgeeks");
}
public static void main(String[] args)
{
String s1 = "Geeks";
concat1(s1); // s1 is not changed
System.out.println("String: " + s1);
StringBuilder s2 = new StringBuilder("Geeks");
concat2(s2); // s2 is changed
System.out.println("StringBuilder: " + s2);
StringBuffer s3 = new StringBuffer("Geeks");
concat3(s3); // s3 is changed
System.out.println("StringBuffer: " + s3);
}
}
Output:
String: Geeks
StringBuilder: Geeksforgeeks
StringBuffer: Geeksforgeeks
Explanation:
Concat1 : In this method, we pass a string “Geeks” and perform “s1 = s1 + ”forgeeks”. The string passed from main() is not changed, this is due to the fact that String is immutable (String不可變是因?yàn)樵贘DK中String類被聲明為一個(gè)final類). Altering the value of string creates another object and s1 in concat1() stores reference of new string. References s1 in main() and cocat1() refer to different strings.
Concat2 : In this method, we pass a string “Geeks” and perform “s2.append(“forgeeks”)” which changes the actual value of the string (in main) to “Geeksforgeeks”. This is due to the simple fact that StringBuilder is mutable and hence changes its value.
Concat3 : StringBuffer is similar to StringBuilder except one difference that StringBuffer is thread safe, i.e., multiple threads can use it without any issue. The thread safety brings a penalty of performance.
問題0:String為什么是不可變的?
String的不可變指的是當(dāng)String變量被再次賦值時(shí)改變的是它的引用地址,而不是修改原來所引用的地址對(duì)應(yīng)的數(shù)據(jù)。
它不可變的原因在于以下源碼:
public final class String
implements java.io.Serializable, Comparable<String>, CharSequence {
/** The value is used for character storage. */
private final char value[];
final class的定義保證它不能被繼承,從而不會(huì)被破壞原有的規(guī)則。
private final char value[]中,private的定義也確保了value數(shù)組不會(huì)被輕易操作,final則只是保證了value的引用地址不變。所以實(shí)現(xiàn)其不可變性的重點(diǎn)在于String類的底層實(shí)現(xiàn)都沒有動(dòng)到這個(gè)數(shù)組里的數(shù)據(jù),并且通過final+private的權(quán)限控制來限制訪問。所以不能簡單解釋為通過final實(shí)現(xiàn)的。
問題1:StringBuffer是線程安全的,StringBuilder是線程不安全的,是使用什么方式實(shí)現(xiàn)的線程安全呢?
任何問題,如果想究其本質(zhì)還需尋根源,所以翻閱一下JDK源碼問題便可知曉:
StringBuffer的源碼如下:
public synchronized StringBuffer append(String str) {
super.append(str);
return this;
}
StringBuilder的源碼如下:
public StringBuilder append(String str) {
super.append(str);
return this;
}
對(duì)過對(duì)比即可清楚看到,StringBuffer的線程安全是使用synchronized關(guān)鍵字實(shí)現(xiàn)的!
問題2: StringBuffer和StringBuilder的擴(kuò)容問題
其實(shí)這兩個(gè)類底層的實(shí)現(xiàn)也基本相同,除了StringBuffer加了synchronized關(guān)鍵字,都用了父類AbstractStringBuilder的append方法
public AbstractStringBuilder append(String str) {
if (str == null)
return appendNull();
int len = str.length();
ensureCapacityInternal(count + len);
str.getChars(0, len, value, count);
count += len;
return this;
}
看一下ensureCapacityInternal的實(shí)現(xiàn):
private void ensureCapacityInternal(int minimumCapacity) {//minimumCapacity為當(dāng)前字符串長度加上追加的字符串長度
// overflow-conscious code
if (minimumCapacity - value.length > 0)//最小需要的字符串長度大于當(dāng)前的容量的話,進(jìn)行擴(kuò)容!
expandCapacity(minimumCapacity); //擴(kuò)容函數(shù)
}
擴(kuò)容函數(shù):
void expandCapacity(int minimumCapacity) {
int newCapacity = value.length * 2 + 2;//第一次擴(kuò)容,當(dāng)前長度*2+2
if (newCapacity - minimumCapacity < 0)//第二次判斷是否需要重新擴(kuò)容!如果第一次擴(kuò)容之后仍舊無法滿足的話,
newCapacity = minimumCapacity;//進(jìn)行二次擴(kuò)容,二次擴(kuò)容就是將當(dāng)前需要的大小直接作為擴(kuò)容的大小
if (newCapacity < 0) { //判斷是否溢出
if (minimumCapacity < 0) // overflow
throw new OutOfMemoryError();
newCapacity = Integer.MAX_VALUE;
}
value = Arrays.copyOf(value, newCapacity);//進(jìn)行字符數(shù)組的復(fù)制,完成擴(kuò)容
}
Arrays.copyOf底層使用的是java.lang.System.arraycopy(Object, int, Object, int, int)實(shí)現(xiàn)的:
public static char[] copyOf(char[] original, int newLength) {
char[] copy = new char[newLength];
System.arraycopy(original, 0, copy, 0,
Math.min(original.length, newLength));
return copy;
}