1. 程式人生 > >[Java基礎]關於String的一些基礎

[Java基礎]關於String的一些基礎

關於String,會有這麼幾個經常提起的概念:==關係符,+號、new、intern,final,字面量、static,方法引數。
String是一種特殊的Java物件。注意,它歸根結底還是一種物件。
首先要知道:
new的含義是:在堆上建立一個新物件。
使用引號宣告的字串都是會直接在字串常量池中生成的;

new一個String的方式雖然不提倡也不常見,但是要知道,new語句之後,是真切地在堆上產生了一個新的物件,該物件的內容即值是一個字串。

case1:通過字面量賦值建立字串

String s1 = "abc";
String s2 = "abc";
問:s1 == s2?

答案:true;
原因:對於物件而言,==關係符比的是引用。所謂引用就是物件的記憶體地址。對於字串來說,通過字面量賦值建立字串時,會優先在常量池中查詢是否已經存在相同的字串,倘若已經存在,棧中的引用直接指向該字串;倘若不存在,則在常量池中生成一個字串,再將棧中的引用指向該字串。所以,在字面量s1賦完值之後,常量區已經生成了字串”abc”;待到字面量s2建立字串時,不需要再生成字串,s2和s1均指向常量池中這個物件。
這是基礎,後面的變種,都是在此基礎上的種種特殊情況。請記住!

case2:通過new的方式建立字串

String s3 = new("abc");
String s4 = "abc"
; 問:s3 == s4?

答案:false;
原因:new的含義使然。String再特殊,也得遵守。這也是一個基本結論,請記住。

case3:+號,及編譯器的優化

String s5 = "abc";
String s6 = "a"+"bc";
String s7 = "a";
String s8 = "bc";
String s9 = s7 + s8;
問:s5 == s6? s5 == s9?

答案:true,false。
原因:在JAVA 1.6之後,常量字串的“+”操作,編譯階段直接會合成為一個字串。於是語句{s6 = “a”+”bc”;}在編譯期間就被等效成了{s6 = “abc”;}但是對於兩個字串變數相加+的{s9 = s7 + s8},在編譯期間是被等效成了{s9 = new StringBuilder().append(s7).append(s8);}所以s9是在堆上建立的一個新物件,故兩者引用不等。

case4:+號,final,及編譯器的優化

String s10 = "abc";
final String s11 = "a";
final String s12 = "bc";
String s13 = s11 + s12;
問:s10 == s13?

答:true。
原因:與case3中語句{s9 = s7 + s8;}不同的是,s7和s8是final型別的;對於final欄位,編譯期直接進行了常量替換,而對於非final欄位則是在執行期進行賦值處理的。這導致在編譯期,語句{s13 = s11 + s12;}被等效成了{s13 = “a” + “bc”};進而會等效為{s13 = “abc”;}

case5:intern,以及Java版本1.7

首先,看一下String類的intern函式:

public native String intern();

它的作用是:
對於堆中的字串物件,可以通過 intern() 方法來將字串新增到常量池中,並返回指向該常量的引用。
JDK1.6以及以前版本中,常量池是放在 Perm 區(屬於方法區)中的。
從JDK 1.7及之後,HotSpot 將常量池從永久代移到了元空間。正因為如此,JDK 1.7 後的intern方法在實現上發生了比較大的改變,JDK 1.7及之後,intern方法還是會先去查詢常量池中是否已經存在該物件內容的字串,如果存在,則返回常量池中的這個字串的引用,這一點與之前沒有區別;區別於JDK1.6,如果在常量池找不到對應的字串,則不會再將字串拷貝到常量池,而只是在常量池中生成一個對原字串的引用,該引用指向堆上的這個String物件。
現將結論放出來:
對於JDK1.6及之前版本來說,對於堆中的字串物件s,s.intern()指向的一直是常量區的引用,而s指向的是堆上的引用,兩者不等;
對於JDK1.7及之後版本來說,對於堆中的字串物件s,在呼叫s.intern()時,如果常量區不存在s內容的字串,則s.intern() == s為true;如果已存在s內容的字串,則s.intern() == s為false。

case5.1:呼叫intern時在常量池中已經存在目標字串
String s18 = "abc";
String s14 = new("abc");
String s16 = new("abc");
問:s14.intern() == s16.intern()? s14.intern() == s18?

答案:都是true,true
原因:執行語句{s14.intern()}時,發現在常量區已經存在字串物件”abc”了;所以返回的引用與s18指向這同一個字串;到執行語句{s16.intern()}以及再次執行語句{s14.intern()}時,都是一樣。故均相等。

case5.2:呼叫intern時在常量池中沒有找到目標字串
String s14 = new("abc")+new("def");
問:s14.intern() == s14?

答案:這就要根據JDK版本來看了。若是JDK1.6,執行語句{s14.intern()}時,因為沒發現,所以會在常量區生成一個字串”abcdef”,並返回該常量區字串的引用。所以JDK1.6環境下答案應該是:false。
在JDK1.7及之後的版本環境下,答案是驗證過的,是:true。

case6:static

    static String s12 = "a";
    static String s13 = "bc";
    static String s14 = s12 + s13;
    public void stringTest(){
    String s15 = "abc";
    Log.e("chwn","s14 == s15?"+(s14 == s15));
    }

答:false。
原因:關鍵字static用在String物件前,相比起其他物件,沒有任何特殊性。

case7:String作為方法引數

public void stringTest(){
    String s16 = new("abc");//"abc";
    changStringTest(s16);
    Log.e("chwn","s16:"+s16);
    }
public void changStringTest(String s){
s = "def";
}
問 printf的結果是啥

答案:abc
原因:**String物件是不可變的。因為String物件具有隻讀特性,所以指向它的任何引用都不可能改變它的值,因此也就不會對其他的引用有什麼影響。**String型別的物件作為方法引數時,儘管傳遞的是物件s16,但實際上傳遞的是引用。方法changStringTest的引數s是字串”abc”的一個新的引用。s的改變,不會影響到同樣是引用的s16。