到底建立了幾個String對象?

來源:互聯網
上載者:User

我們首先來看一段代碼:<br />Java代碼<br />String str=new String("abc");<br />緊接著這段代碼之後的往往是這個問題,那就是這行代碼究竟建立了幾個String對象呢?相信大家對這道題並不陌生,答案也是眾所周知的,2個。接下來我們就從這道題展開,一起回顧一下與建立String對象相關的一些JAVA知識。<br />我們可以把上面這行代碼分成String str、=、"abc"和new String()四部分來看待。String str只是定義了一個名為str的String類型的變數,因此它並沒有建立對象;=是對變數str進行初始化,將某個對象的引用(或者叫控制代碼)賦值給它,顯然也沒有建立對象;現在只剩下new String("abc")了。那麼,new String("abc")為什麼又能被看成"abc"和new String()呢?我們來看一下被我們調用了的String的構造器:<br />Java代碼<br />public String(String original) {<br /> //other code ...<br />}<br />大家都知道,我們常用的建立一個類的執行個體(對象)的方法有以下兩種:<br />使用new建立對象。<br />調用Class類的newInstance方法,利用反射機制建立對象。<br />我們正是使用new調用了String類的上面那個構造器方法建立了一個對象,並將它的引用賦值給了str變數。同時我們注意到,被調用的構造器方法接受的參數也是一個String對象,這個對象正是"abc"。由此我們又要引入另外一種建立String對象的方式的討論——引號內包含文本。<br />這種方式是String特有的,並且它與new的方式存在很大區別。<br />Java代碼<br />String str="abc";<br />毫無疑問,這行代碼建立了一個String對象。<br />Java代碼<br />String a="abc";<br />String b="abc";<br />那這裡呢?答案還是一個。<br />Java代碼<br />String a="ab"+"cd";<br />再看看這裡呢?答案是三個。有點奇怪嗎?說到這裡,我們就需要引入對字串池相關知識的回顧了。<br />在JAVA虛擬機器(JVM)中存在著一個字串池,其中儲存著很多String對象,並且可以被共用使用,因此它提高了效率。由於String類是final的,它的值一經建立就不可改變,因此我們不用擔心String對象共用而帶來程式的混亂。字串池由String類維護,我們可以調用intern()方法來訪問字串池。<br />我們再回頭看看String a="abc";,這行代碼被執行的時候,JAVA虛擬機器首先在字串池中尋找是否已經存在了值為"abc"的這麼一個對象,它的判斷依據是String類equals(Object obj)方法的傳回值。如果有,則不再建立新的對象,直接返回已存在對象的引用;如果沒有,則先建立這個對象,然後把它加入到字串池中,再將它的引用返回。因此,我們不難理解前面三個例子中頭兩個例子為什麼是這個答案了。<br />對於第三個例子:<br />Java代碼<br />String a="ab"+"cd";<br />"ab"和"cd"分別建立了一個對象,它們經過“+”串連後又建立了一個對象"abcd",因此一共三個,並且它們都被儲存在字串池裡了。<br />現在問題又來了,是不是所有經過“+”串連後得到的字串都會被添加到字串池中呢?我們都知道“==”可以用來比較兩個變數,它有以下兩種情況:<br />如果比較的是兩個基本類型(char,byte,short,int,long,float,double,boolean),則是判斷它們的值是否相等。<br />如果表較的是兩個物件變數,則是判斷它們的引用是否指向同一個對象。<br />下面我們就用“==”來做幾個測試。為了便於說明,我們把指向字串池中已經存在的對象也視為該對象被加入了字串池:<br />Java代碼<br />public class StringTest {<br /> public static void main(String[] args) {<br /> String a = "ab";// 建立了一個對象,並加入字串池中<br /> System.out.println("String a = /"ab/";");<br /> String b = "cd";// 建立了一個對象,並加入字串池中<br /> System.out.println("String b = /"cd/";");<br /> String c = "abcd";// 建立了一個對象,並加入字串池中 </p><p> String d = "ab" + "cd";<br /> // 如果d和c指向了同一個對象,則說明d也被加入了字串池<br /> if (d == c) {<br /> System.out.println("/"ab/"+/"cd/" 建立的對象 /"加入了/" 字串池中");<br /> }<br /> // 如果d和c沒有指向了同一個對象,則說明d沒有被加入字串池<br /> else {<br /> System.out.println("/"ab/"+/"cd/" 建立的對象 /"沒加入/" 字串池中");<br /> } </p><p> String e = a + "cd";<br /> // 如果e和c指向了同一個對象,則說明e也被加入了字串池<br /> if (e == c) {<br /> System.out.println(" a +/"cd/" 建立的對象 /"加入了/" 字串池中");<br /> }<br /> // 如果e和c沒有指向了同一個對象,則說明e沒有被加入字串池<br /> else {<br /> System.out.println(" a +/"cd/" 建立的對象 /"沒加入/" 字串池中");<br /> } </p><p> String f = "ab" + b;<br /> // 如果f和c指向了同一個對象,則說明f也被加入了字串池<br /> if (f == c) {<br /> System.out.println("/"ab/"+ b 建立的對象 /"加入了/" 字串池中");<br /> }<br /> // 如果f和c沒有指向了同一個對象,則說明f沒有被加入字串池<br /> else {<br /> System.out.println("/"ab/"+ b 建立的對象 /"沒加入/" 字串池中");<br /> } </p><p> String g = a + b;<br /> // 如果g和c指向了同一個對象,則說明g也被加入了字串池<br /> if (g == c) {<br /> System.out.println(" a + b 建立的對象 /"加入了/" 字串池中");<br /> }<br /> // 如果g和c沒有指向了同一個對象,則說明g沒有被加入字串池<br /> else {<br /> System.out.println(" a + b 建立的對象 /"沒加入/" 字串池中");<br /> }<br /> }<br />}<br />運行結果如下:<br />String a = "ab";<br />String b = "cd";<br />"ab"+"cd" 建立的對象 "加入了" 字串池中<br />a +"cd" 建立的對象 "沒加入" 字串池中<br />"ab"+ b 建立的對象 "沒加入" 字串池中<br />a + b 建立的對象 "沒加入" 字串池中<br />從上面的結果中我們不難看出,只有使用引號包含文本的方式建立的String對象之間使用“+”串連產生的新對象才會被加入字串池中。對於所有包含new方式建立對象(包括null)的“+”串連運算式,它所產生的新對象都不會被加入字串池中,對此我們不再贅述。因此我們提倡大家用引號包含文本的方式來建立String對象以提高效率,實際上這也是我們在編程中常採用的。<br />接下來我們再來看看intern()方法,它的定義如下:<br />Java代碼<br />public native String intern();<br />這是一個本地方法。在調用這個方法時,JAVA虛擬機器首先檢查字串池中是否已經存在與該對象值相等對象存在,如果有則返回字串池中對象的引用;如果沒有,則先在字串池中建立一個相同值的String對象,然後再將它的引用返回。<br />我們來看這段代碼:<br />Java代碼<br />public class StringInternTest {<br /> public static void main(String[] args) {<br /> // 使用char數組來初始化a,避免在a被建立之前字串池中已經存在了值為"abcd"的對象<br /> String a = new String(new char[] { 'a', 'b', 'c', 'd' });<br /> String b = a.intern();<br /> if (b == a) {<br /> System.out.println("b被加入了字串池中,沒有建立對象");<br /> } else {<br /> System.out.println("b沒被加入字串池中,建立了對象");<br /> }<br /> }<br />}<br />運行結果:<br />b沒被加入字串池中,建立了對象<br />如果String類的intern()方法在沒有找到相同值的對象時,是把當前對象加入字串池中,然後返回它的引用的話,那麼b和a指向的就是同一個對象;否則b指向的對象就是JAVA虛擬機器在字串池中建立的,只是它的值與a相同罷了。上面這段代碼的運行結果恰恰印證了這一點。<br />最後我們再來說說String對象在JAVA虛擬機器(JVM)中的儲存,以及字串池與堆(heap)和棧(stack)的關係。我們首先回顧一下堆和棧的區別:<br />棧(stack):主要儲存基本類型(或者叫內建類型)(char、byte、short、int、long、float、double、boolean)和對象的引用,資料可以共用,速度僅次於寄存器(register),快於堆。<br />堆(heap):用於儲存物件。<br />我們查看String類的源碼就會發現,它有一個value屬性,儲存著String對象的值,類型是char[],這也正說明了字串就是字元的序列。<br />當執行String a="abc";時,JAVA虛擬機器會在棧中建立三個char型的值'a'、'b'和'c',然後在堆中建立一個String對象,它的值(value)是剛才在棧中建立的三個char型值組成的數組{'a','b','c'},最後這個新建立的String對象會被添加到字串池中。如果我們接著執行String b=new String("abc");代碼,由於"abc"已經被建立並儲存於字串池中,因此JAVA虛擬機器只會在堆中新建立一個String對象,但是它的值(value)是共用前一行代碼執行時在棧中建立的三個char型值值'a'、'b'和'c'。<br />說到這裡,我們對於篇首提出的String str=new String("abc")為什麼是建立了兩個對象這個問題就已經相當明了了。 

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.