例子說明String的堆(heap)棧(stack)信息
String是一個特殊的包裝類數(shù)據(jù)。即可以用String str = new String("abc");的形式來創(chuàng)建,也可以用String str = "abc";的形式來創(chuàng)建。前者是規(guī)范的類的創(chuàng)建過程,即在Java中,一切都是對象,而對象是類的實例,全部通過new()的形式來創(chuàng)建。Java 中的有些類,如DateFormat類,可以通過該類的getInstance()方法來返回一個新創(chuàng)建的類,似乎違反了此原則。其實不然。該類運用了單 例模式來返回類的實例,只不過這個實例是在該類內(nèi)部通過new()來創(chuàng)建的,而getInstance()向外部隱藏了此細節(jié)。那為什么在String str = "abc";中,并沒有通過new()來創(chuàng)建實例,是不是違反了上述原則?其實沒有。關(guān)于String str = "abc"的內(nèi)部工作。Java內(nèi)部將此語句轉(zhuǎn)化為以下幾個步驟:
(1)先定義一個名為str的對String類的對象引用變量:String str;
(2)在棧中查找有沒有存放值為"abc"的地址,如果沒有,則開辟一個存放字面值為"abc"的地址,接著創(chuàng)建一個新的String類的對象o,并 將o 的[url=]字符串[/url]值指向這個地址,而且在棧中這個地址旁邊記下這個引用的對象o。如果已經(jīng)有了值為"abc"的地址,則查找對象o,并返 回o的地址。
(3)將str指向?qū)ο髈的地址。
值得注意的是,一般String類中字符串值都是直接存值的。但像String str = "abc";這種場合下,其字符串值卻是保存了一個指向存在棧中數(shù)據(jù)的引用!
為了更好地說明這個問題,我們可以通過以下的幾個代碼進行驗證。
String str1 = "abc";
String str2 = "abc";
System.out.println(str1==str2); //true
注意,我們這里并不用str1.equals(str2);的方式,因為這將比較兩個字符串的值是否相等。==號,根據(jù)JDK的說明,只有在兩個引用都指向了同一個對象時才返回真值。而我們在這里要看的是,str1與str2是否都指向了同一個對象。
圖解:


結(jié)果說明,JVM創(chuàng)建了兩個引用str1和str2,但只創(chuàng)建了一個對象,而且兩個引用都指向了這個對象。
我們再來更進一步,將以上代碼改成:
String str1 = "abc";
String str2 = "abc";
str1 = "bcd";
System.out.println(str1 + "," + str2); //bcd, abc
System.out.println(str1==str2); //false
這就是說,賦值的變化導致了類對象引用的變化,str1指向了另外一個新對象!而str2仍舊指向原來的對象。上例中,當我們將str1的值改為"bcd"時,JVM發(fā)現(xiàn)在棧中沒有存放該值的地址,便開辟了這個地址,并創(chuàng)建了一個新的對象,其字符串的值指向這個地址。
事實上,String類被設(shè)計成為不可改變(immutable)的類。如果你要改變其值,可以,但JVM在運行時根據(jù)新值悄悄創(chuàng)建了一個新對象,然 后將這個對象的地址返回給原來類的引用。這個創(chuàng)建過程雖說是完全自動進行的,但它畢竟占用了更多的時間。在對時間要求比較敏感的環(huán)境中,會帶有一定的不良 影響。
再修改原來代碼:
String str1 = "abc";
String str2 = "abc";
str1 = "bcd";
String str3 = str1;
System.out.println(str3); //bcd
String str4 = "bcd";
System.out.println(str1 == str4); //true

str3 這個對象的引用直接指向str1所指向的對象(注意,str3并沒有創(chuàng)建新對象)。當str1改完其值后,再創(chuàng)建一個String的引用str4,并指向 因str1修改值而創(chuàng)建的新的對象。可以發(fā)現(xiàn),這回str4也沒有創(chuàng)建新的對象,從而再次實現(xiàn)棧中數(shù)據(jù)的共享。
我們再接著看以下的代碼。
String str1 = new String("abc");
String str2 = "abc";
System.out.println(str1==str2); //false

創(chuàng)建了兩個引用。創(chuàng)建了兩個對象。兩個引用分別指向不同的兩個對象。
String str1 = "abc";
String str2 = new String("abc");
System.out.println(str1==str2); //false
創(chuàng)建了兩個引用。創(chuàng)建了兩個對象。兩個引用分別指向不同的兩個對象。
以上兩段代碼說明,只要是用new()來新建對象的,都會在堆中創(chuàng)建,而且其字符串是單獨存值的,即使與棧中的數(shù)據(jù)相同,也不會與棧中的數(shù)據(jù)共享。
posted on 2008-09-04 14:22 nonels 閱讀(496) 評論(0) 編輯 收藏 所屬分類: J2SE