String
String有兩種用法:
# 用法1
String a = "abc";//"abc"存在於常量池. 如果之後有String b = "abc";b也是指向常量池的"abc"
----------------------------------------------------------------------------------
# 用法2
String a = new String("abc");//"abc"存在於堆.如果之後有String b = new String("abc");b是在堆中重新開闢內存存儲"abc"
------------------------------------比較-------------------------------------------
String a = "123";
String b = "123";
String c = new String("123");
String d = new String("123");
System.out.printf(a == b); //true 因爲比較的是變量(a、b)指向常量池的字符串的值
System.out.printf(a == c); //false 比較的是引用,很明顯a和c的引用是不一樣的,c是後面new出來的
System.out.printf(c == d); //false 道理差不多同上
System.out.printf(c.equals(d)); //true 比較的是引用指向的值;雖然c、d的引用不一樣,但是他們指向的值是一樣的。(這裏要明確,這倆個"123"在堆中是不同一個內存的)
String賦值的時候,先在常量池中查找有沒有當前需要的字符串,如果有,則直接指向;如果沒有,則創建一個新的字符串。
#思考字符串被創建過程
String a = "ab";
String b = "abc";
a = a + "c";
System.out.printf(a == b); //false
//這裏爲什麼 a = "abc",b ="abc" 怎麼返回false
//因爲a = a + "c";的時候,雖然a的值變爲"abc"了,但a不是指向b創建在常量池中的"abc",而是重新建了一個"abc"的對象,這兩個對象是不一樣的地址,所以返回false。
//更加細節的話,我的理解是從賦值順序來理解:
a = a + "c"; 首先是a的值"ab"加上"c"組合成"abc"這個字符串,然後再存進常量池,再然後直接賦值給變量a。所以這裏不像往常那樣先查找常量池有沒有自己需要的字符串,再進行指向。
StringBuffer
StringBuffer有append()、insert()、reverse()等常用方法。
StringBuffer e = new StringBuffer("abc");
e.append("d");
System.out.print(e); //輸出abcd
System.out.print(e.reverse()); //輸出dcba 倒序輸出
e.insert(1,"dd"); //在位置1插入dd
System.out.print(e); //輸出addbcd
StringBuffer的值不是在常量池中,而是在堆中,因它是new出來的對象,相應的值是在對中的,引用是在棧中。
就如上面的代碼e.append("d");直接修改原來的對象中連接上新的字符串"d",而不是生成新對象。
StringBuilder
StringBuilder實際上與StringBuffer基本相似。不同的是StringBuffer是線程安全的;而StringBuilder沒有線程安全功能,但性能高於StringBuffer。(俗話說,性能和安全性同於魚與熊掌不可兼得)
我們可以從其實現方法中去了解:
在實現方法中添加了synchronized,則說明給該方法加了鎖,保證線程安全。