轉(zhuǎn)載 http://sarin.iteye.com/blog/603684 這篇文章講的太好了!
先看一段代碼:
public class Test {
public static void main(String[] args){
String str = "abc";
String str1 = "abc";
String str2 = new String("abc");
System.out.println(str == str1);
System.out.println(str1 == "abc");
System.out.println(str2 == "abc");
System.out.println(str1 == str2);
System.out.println(str1.equals(str2));
System.out.println(str1 == str2.intern());
System.out.println(str2 == str2.intern());
System.out.println(str1.hashCode() == str2.hashCode());
}
}
Java語言使用內(nèi)存的時候庄新,棧內(nèi)存主要保存以下內(nèi)容:基本數(shù)據(jù)類型和對象的引用鞠眉,而堆內(nèi)存存儲對象,棧內(nèi)存的速度要快于堆內(nèi)存摄咆》惭粒總結(jié)成一句話就是:引用在棧而對象在堆人断。
Java中的比較有兩種吭从,是==和equals()方法,equals()是Object類的方法恶迈,定義在Object類中的equals()方法是如下實現(xiàn)的:
public boolean equals(Object obj){
return (this==obj);
}
String類重寫了equals()方法涩金,改變了這些類型對象相等的原則,即判斷對象是否相等依據(jù)的原則為判斷二者的內(nèi)容是否相等暇仲。
了解以上內(nèi)容后我們來說說String步做,String類的本質(zhì)是字符數(shù)組char[],其次String類是final的奈附,是不可被繼承的全度,這點可能被大多數(shù)人忽略,再次String是特殊的封裝類型斥滤,使用String時可以直接賦值将鸵,也可以用new來創(chuàng)建對象勉盅,但是這二者的實現(xiàn)機制是不同的。還有一個String池的概念顶掉,Java運行時維護一個String池草娜,池中的String對象不可重復,沒有創(chuàng)建痒筒,有則作罷宰闰。String池不屬于堆和棧,而是屬于常量池簿透。
下面分析上方代碼的真正含義:
String str = "abc";
String str1= "abc";
第一句的真正含義是在String池中創(chuàng)建一個對象”abc”移袍,然后引用時str指向池中的對象”abc”。第二句執(zhí)行時老充,因為”abc”已經(jīng)存在于String池了咐容,所以不再創(chuàng)建,則str==str1返回true就明白了蚂维。str1==”abc”肯定正確了戳粒,在String池中只有一個”abc”,而str和str1都指向池中的”abc”虫啥,就是這個道理蔚约。
String str2 = new String("abc");
單獨這句話創(chuàng)建了2個String對象,而基于上面兩句涂籽,只在棧內(nèi)存創(chuàng)建str2引用苹祟,在堆內(nèi)存上創(chuàng)建一個String對象,內(nèi)容是”abc”评雌,而str2指向堆內(nèi)存對象的首地址树枫。
下面就是str2==”abc”的問題了,顯然不對景东,”abc”是位于String池中的對象砂轻,而str2指向的是堆內(nèi)存的String對象,==判斷的是地址斤吐,肯定不等了搔涝。
str1.equals(str2),這個是對的和措,前面說過庄呈,String類的equals重寫了Object類的equals()方法,實際就是判斷內(nèi)容是否相同了派阱。
下面說下intern()方法诬留,在JavaDoc文檔中,這樣描述了intern()方法:返回字符串對象的規(guī)范化表示形式。怎么理解這句話文兑?實際上過程是這樣進行的:該方法現(xiàn)在String池中查找是否存在一個對象傀广,存在了就返回String池中對象的引用。
那么本例中String池存在”abc”彩届,則調(diào)用intern()方法時返回的是池中”abc”對象引用伪冰,那么和str/str1都是等同的,和str2就不同了樟蠕,因為str2指向的是堆內(nèi)存贮聂。
hashCode()方法是返回字符串內(nèi)容的哈希碼,既然內(nèi)容相同寨辩,哈希碼必然相同吓懈,那他們就相等了,這個容易理解靡狞。
再看下面的例子:
public class Test {
private static String str = "abc";
public static void main(String[] args) {
String str1 = "a";
String str2 = "bc";
String combo = str1 + str2;
System.out.println(str == combo);
System.out.println(str == combo.intern());
}
}
這個例子用來說明用+連接字符串時耻警,實際上是在堆內(nèi)容創(chuàng)建對象,那么combo指向的是堆內(nèi)存存儲”abc”字符串的空間首地址甸怕,顯然str==combo是錯誤的甘穿,而str==combo.intern()是正確的,在String池中也存在”abc”梢杭,那就直接返回了温兼,而str也是指向String池中的”abc”對象的。此例說明任何重新修改String都是重新分配內(nèi)存空間武契,這就使得String對象之間互不干擾募判。也就是String中的內(nèi)容一旦生成不可改變,直至生成新的對象咒唆。
同時問題也來了届垫,使用+連接字符串每次都生成新的對象,而且是在堆內(nèi)存上進行全释,而堆內(nèi)存速度比較慢(相對而言)装处,那么再大量連接字符串時直接+是不可取的,當然需要一種效率高的方法恨溜。Java提供的StringBuffer和StringBuilder就是解決這個問題的符衔。區(qū)別是前者是線程安全的而后者是非線程安全的,StringBuilder在JDK1.5之后才有糟袁。不保證安全的StringBuilder有比StringBuffer更高的效率。
自JDK1.5之后躺盛,Java虛擬機執(zhí)行字符串的+操作時项戴,內(nèi)部實現(xiàn)也是StringBuilder,之前采用StringBuffer實現(xiàn)槽惫。