關(guān)于String需要知道的二三事

關(guān)于String需要知道的二三事

標(biāo)簽: Java基礎(chǔ)


原文鏈接:關(guān)于String需要知道的二三事 轉(zhuǎn)載請(qǐng)注明出處~

類型

在Java中,字符串類型String不是基本數(shù)據(jù)類型,也不是字符數(shù)組,是Java類庫(kù)提供的一個(gè)類

不可變字符串

String是不可變字符串,就是說(shuō)當(dāng)我們用重載的運(yùn)算符“+”來(lái)連接兩個(gè)String的時(shí)候,其實(shí)是新建了一個(gè)String。

String s = "abcd";
s = s+1;
System.out.print(s);// result : abcd1

JVM是這樣來(lái)解析這段代碼的:

  • 首先創(chuàng)建一個(gè)引用s指向“abcd”這個(gè)字符串
  • 然后新建一個(gè)“abcd1”的字符串,s指向新的字符串,原來(lái)的字符串等待垃圾回收。
  • 之所以說(shuō)是不可變字符串是因?yàn)樽址旧聿](méi)有發(fā)生變化,在運(yùn)行時(shí)重新再在堆中分配內(nèi)存速度就會(huì)慢,效率就會(huì)很低。
    與其相對(duì)應(yīng)的是StringBuilderStringBuffer,是可變字符串,每當(dāng)我們用它們對(duì)字符串做操作時(shí),實(shí)際上是在一個(gè)對(duì)象上操作的,這樣就不會(huì)像String一樣創(chuàng)建一些而外的對(duì)象進(jìn)行操作了,當(dāng)然速度就快了。

一個(gè)特殊的例子

String str = “This is only a” + “ simple” + “ test”;
StringBuffer builder = new StringBuilder(“This is only a”).append(“ simple”).append(“ test”);

這個(gè)時(shí)候會(huì)發(fā)現(xiàn)String的速度也很快,StringBuffer并沒(méi)有明顯優(yōu)勢(shì),這是JVM給我們玩的小把戲,實(shí)際上:
   String str = “This is only a” + “ simple” + “test”;
    其實(shí)就是:
   String str = “This is only a simple test”;
所以不需要太多的時(shí)間了。但大家這里要注意的是,如果你的字符串是來(lái)自另外的String對(duì)象的話,速度就沒(méi)那么快了,譬如:

String str2 = “This is only a”;
String str3 = “ simple”;
String str4 = “ test”;
String str1 = str2 +str3 + str4;

這時(shí)候JVM會(huì)規(guī)規(guī)矩矩的按照原來(lái)的方式去做。

關(guān)于StringBuffer和StringBuilder

  • StringBuilder:線程非安全的
  • StringBuffer:線程安全的

當(dāng)我們?cè)谧址彌_去被多個(gè)線程使用是,JVM不能保證StringBuilder的操作是安全的,雖然他的速度最快,但是可以保證StringBuffer是可以正確操作的。當(dāng)然大多數(shù)情況下就是我們是在單線程下進(jìn)行的操作,所以大多數(shù)情況下是建議用StringBuilder而不用StringBuffer的,就是速度的原因。

總結(jié)如下:

  • 如果要操作少量的數(shù)據(jù)用String
  • 單線程操作字符串緩沖區(qū) 下操作大量數(shù)據(jù) StringBuilder
  • 多線程操作字符串緩沖區(qū) 下操作大量數(shù)據(jù) StringBuffer

常量池

對(duì)于字符串:其對(duì)象的引用都是存儲(chǔ)在棧中的,如果是編譯期已經(jīng)創(chuàng)建好(直接用雙引號(hào)定義的)的就存儲(chǔ)在常量池中,如果是運(yùn)行期(new出來(lái)的)才能確定的就存儲(chǔ)在堆中。

public static void main(String[] args){
        String a = "abc";
        String b = "abc";
        String c = new String("abc");
        String d = new String("abc");
        System.out.println(a==b); //true
        System.out.println(a==c); //false
        System.out.println(d==c); //false
        System.out.println(d.equals(c)); //true
        System.out.println(a.equals(c)); //true
    }

結(jié)果分析:

  1. 創(chuàng)建a的時(shí)候首先會(huì)在常量池中查找是否有“abc”這個(gè)字符串,如果有,就將a指向該字符串,否則就會(huì)在常量池中新建一個(gè)“abc”
  2. 由于前面已經(jīng)新建了“abc”字符串,所以引用b直接指向常量池中的“abc”
  3. 通過(guò)new創(chuàng)建的字符串會(huì)有兩個(gè)步驟:
    1. 會(huì)先在堆中再創(chuàng)建一個(gè)常量池中此 ”abc” 對(duì)象。c指向堆中的對(duì)象
    2. 然后去常量池中查找是否已經(jīng)有了 ”abc”對(duì)象,如果沒(méi)有則在常量池中創(chuàng)建一個(gè)此字符串對(duì)象
  4. 所以c和d都指向堆中的不同對(duì)象,但是由于所存儲(chǔ)的內(nèi)容相同所以可以equals

一個(gè)例子:

String a = "abc";
String e = "abcd";
String f = a+"d";
String g = "abc" + "d";
System.out.println(e == f); //false
System.out.println(e == g); //true

意不意外,驚不驚喜!

  1. 我們前文分析提到,在執(zhí)行String g = "abc" + "d"這種操作時(shí),是JVM和我們玩的小trick,直接將常量拼接了,所以g就是在常量區(qū)中的“abcd”,和d是相同的
  2. 在執(zhí)行String f = a+"d";時(shí),是涉及到變量(不全是常量)的相加,所以會(huì)生成新的對(duì)象,其內(nèi)部實(shí)現(xiàn)是先new一個(gè)StringBuilder,然后 append(a),append("d");然后讓f引用toString()返回的對(duì)象,所以f指向的是堆中的對(duì)象,e是指向常量區(qū)中的對(duì)象。

That's all,enjoy it~
歡迎訪問(wèn)博主個(gè)人博客:http://kongdehui.com/ ~~~

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容