1. 程式人生 > >String的賦值操作,intern,==等的關係

String的賦值操作,intern,==等的關係

很多人可能都會對String的==操作感到非常迷茫,這個的話, 首先我們來看一下這個規律。

jdk1.6

1)如果對String變數賦值字面量,比如String a = “abcdef”,那麼這個時候會首先到方法區的常量池中間找abcdef是否存在,如果存在,則返回這個物件的引用。如果不存在,則首先在常量池中建立這個物件,再返回這個物件的引用。

2)如果是new,則會在堆中建立這個物件。如果對這個new建立的物件使用intern,則會在方法區中建立一個物件。比如String b = new String(“123456”),在堆中建立一個String物件b,b.intern(),去方法區的常量區檢視是否存在123456,不存在建立,存在則不建立,然後返回這個常量區的123456字串的引用(不修改b)

3)==比較的是地址,而不是內容,equals比較的是內容(對於字串而言)

jdk1.7,方法區的常量區已經放到了堆記憶體中了,因此有區別,區別主要是在intern裡面。

1)如果對String變數賦值字面量,若這個字面量是第一次使用,這個和jdk1.6類似。但是有一個區別,區別就是如果常量池存在了這麼個物件,那麼可能返回的是一個堆記憶體物件的引用,原因就是intern方法,具體原因往下看。

2)對於new建立的字串,比如String b = new String("abcdefgh"),會在堆中建立這麼一個物件,同時也會在常量池建立abcdefgh字串物件。這個時候如果你呼叫b.intern(),那麼幾乎是沒有用處的,這個b.intern()的作用僅僅是檢查常量池是否存在abcdefgh這個字串,這裡是存在的,直接返回常量池中字串物件abcdefgh的引用即可。如果不存在呢?這個地方要注意了!它會把b的引用複製到常量池,然後返回b的引用(jdk1.6是在常量池直接建立一個物件),這個時候就會產生一種效果。

什麼效果呢?往下看一個具體的例子

什麼都先不說,先看下面這個引入的例子:

  1. String str1 = new String("SEU")+ new String("Calvin");      
  2. System.out.println(str1.intern() == str1);   
  3. System.out.println(str1 == "SEUCalvin");  

本人JDK版本1.8,輸出結果為:

  1. true
  2. true
再將上面的例子加上一行程式碼:
  1. String str2 = "SEUCalvin";//新加的一行程式碼,其餘不變
  2. String str1 = new
     String("SEU")+ new String("Calvin");      
  3. System.out.println(str1.intern() == str1);   
  4. System.out.println(str1 == "SEUCalvin");   
再執行,結果為:
  1. false
  2. false
這個例子呢,是從別的文章摘抄過來的,不過他沒有解釋原因,我來幫他解釋下吧。

jdk1.8和jdk1.7是非常類似的。第一次執行的結果是true,true,為什麼呢?

先看第一段程式碼,第一行String str1 = new String("SEU")+new String("Calvin");這裡jvm作了什麼工作呢?jvm在堆中建立了3個物件,內容為SEU和內容為Calvin以及內容為SEUCalvin的物件。同時,在常量池中建立了兩個物件,SEU和Calvin。好,那麼str1.intern()==str1為什麼是true呢?回到我們前面所講的加粗內容:它會把b的引用複製到常量池,然後返回b的引用,這個時候就會產生一種效果。 來看,str1的內容SEUCalvin在常量池是不存在的,於是,我們將str1的引用複製到常量池,並返回這個引用,也就是返回str1,那麼str1和str1相等嗎?當然相等。再看第三行,str1==“SEUCalvin”,這裡也是true,為什麼呢?按照jdk1.6,這裡應該是false啊。首先你要明白,“SEUCalvin”這是個字串常量,它會到常量池中去查詢是否存在這麼個物件,結果發現存在(第二行的str1.intern()在常量池建立了這麼一個引用),然後返回了這麼個引用,這個引用還是str1啊,所以還是true;

第二段程式碼為什麼都變成了false了呢?首先我們要明白,==比較的都是地址,因此返回false肯定是地址不同,在分析之前,我們先看一個簡單的例子:

String a = new String("ab1234rcdefgh");
System.out.println(a.intern()==a);
這裡輸出啥?如果你的回答是false,我可以恭喜你,前面理解的還不錯,因為這裡是的new String("ab1234rcdefgh")建立了兩個物件,一個在堆中,一個在常量池中,因此,a.intern()返回的是一個在常量池的字串物件的引用,而不是a引用本身,因此這裡的輸出是false;

好,我們來看第二段程式碼。第二段程式碼就是多了一行,String str2 = "SEUCalvin";這一行的作用可大了啊。看我們的jdk1.7的規律的第一行,是不是說了,如果第一次使用這個字面量,我們會在常量池中建立這麼個物件對吧。那麼這個物件已經建立了,後面的new操作也就不會在常量池建立物件了,同時intern操作也不會將引用賦值過去了,而是檢查發現這個字串已經在常量池了,那麼直接返回這個字串的引用就行了。所以,str1.intern()返回的實際上是str2,“SEUCalvin”也是str2,所以都是ifalse了。

再來看一個簡單的例子加深理解。

String c = new String("123")+new String("456");
String d = "123456";
System.out.println(c==d);
System.out.println(c.intern()==d);

這裡的輸出是false;true。

原因?c物件的建立伴隨著在堆中建立123,456,123456物件,同時也伴隨著在常量池中建立123,456物件。d的建立伴隨著在常量池闖將123456物件。因此c和d是不同的引用,是不同的地址。c.intern()操作返回的地址就是d啊,這個很清楚了啊。intern()操作回去常量池查詢是否存在這麼個字串123456,存在的直接返回引用。不存在,在jdk7中是要將這個字串物件的引用複製過去(jdk1.6是在常量池建立字串物件)。而這裡是存在的,返回的引用就是d,所以肯定是true;