java編程中經(jīng)常使用String佛吓,StringBuffer和StringBuilder或許僅常用其中一個,其實(shí)StringBuffer與StringBuilder的方法都差不多垂攘, 傻傻的并不能區(qū)分這兩個有什么區(qū)別维雇。最近偶遇到順便查閱資料總結(jié)下。
String晒他、StringBuffer與StringBuilder
1吱型、可變與不可變
String是不可變的≡山觯看下String的源碼即可知津滞。
而StringBuffer與StringBuilder雖也是final铝侵,但都是繼承自AbstractStringBuilder,其append實(shí)現(xiàn)方法都是重寫AbstractStringBuilder里的触徐。
2咪鲜、線程安全與不安全
String中的對象是不可變的,可看為常量锌介,所以是線程安全的
StringBuffer也是線程安全的嗜诀。其主要方法都加了synchronized,顯然線程安全孔祸。部分源碼如下:
而StringBuilder的方法沒有加synchronized隆敢,在多線程下是不安全的。
線程安全與不安全造成了StringBuilder與StringBuffer在效率方面有區(qū)別崔慧。
這三者的速度排序是這樣的:
String<StringBuffer<StringBuilder
所以拂蝎,在經(jīng)常要操作字符串的情況下,多線程下惶室,盡量用StringBuffer温自,單線程下,則盡量使用StringBuilder
或許會有疑問:String為什么是最慢的皇钞,String不是經(jīng)车棵冢看到String = str1+str2嗎?為什么是不可變的夹界?
其實(shí)jvm在實(shí)現(xiàn)String的加減并不是簡單的加減馆里。
Jvm有一個常量池,用來存放常量可柿,并且是線程共享的鸠踪。
如:
String s2= "abc";
實(shí)際上jvm會在常量池創(chuàng)建“abc”這個常量對象。而s2再次賦值為abc時复斥,jvm會先到常量池查找营密,找到后,并不會再次創(chuàng)建這個對象目锭,而只是返回了abc這個對象的引用而已评汰,即s1和s2此時指的同一個對象,其值也是一樣痢虹。
String s3 = new String("abc");
此時創(chuàng)建兩個對象键俱,一個存放在堆中(String對象),一個存放在常量池中("abc"),還有一個對象引用s3世分,存放在棧编振。
這個時候如果在new一個對象,如下:
此時只創(chuàng)建一個對象,和存放在棧上的引用s4踪央,jvm檢測到常量池里存有abc了臀玄,直接把a(bǔ)bc引用給了s4.
String s5 = "ab"+"cd";
這個加法編譯時會自動優(yōu)化了。因?yàn)閮蓚€常量的值都是已經(jīng)確定了的畅蹂。相當(dāng)于String s5="abcd";所以這個過程還是很快的
但是如果是下面的加法;
String s6 = "ab";
String s7 = "cd";
String s8 = s6+s7;
String s9 ="abcd";
則可就不一樣了健无。s6和s7都是對象,編譯時其值不是確定的液斜,所以不會自動優(yōu)化累贤。而底層對這個加法的實(shí)現(xiàn)是這樣的。運(yùn)行時jvm創(chuàng)建一個StringBuilder少漆。并用s6所指的字符串完成初始化臼膏,然后用這個StringBuilder對象調(diào)用append方法合并s7的字符串cd,在調(diào)用StringBuilder的toString方法完成類型轉(zhuǎn)換示损,形成的新對象即引用給s8渗磅。這會導(dǎo)致s8!=s9.所以String的加法整體上說沒有StringBuffer和StringBuilder的效率高检访。
final String s1 = "cd;
String s2= "ab" + s1;
String s3 = "abcd";
System.out.println("s2 = s3 : "+ (s2 == s3));
上面的str1在編譯時會被認(rèn)為是常量始鱼,所以s2+s1這里會被編譯優(yōu)化。所以導(dǎo)致最后s2和s3是同一個對象脆贵。運(yùn)行結(jié)果為true