一個簡單但不簡約的String面試題.

我相信每個Java程序員對String都熟悉的不能再熟悉了,可以說只要敲代碼,就繞不過String.
但是 越是我們熟悉的人,我們就越容易忽略掉他的一些細節.
下面是一個簡單但不簡約的String的面試題.
小夥伴們,自習思考一下,看看答案跟你們想的是不是很不一樣吶~~

public class TestString1 {
    public static void main(String[] args) {
        String s1 = new String("1") + new String("2");
        String s2 = "12";
        System.out.println(s1 == s2);
        System.out.println("-----------------------------------");
        String s3 = new String("1") + new String("3");
        s3.intern();
        String s4 = "13";
        System.out.println(s3 == s4);
        System.out.println("-----------------------------------");
        String s5 = new String("1") + new String("4");
        String s6 = s5.intern();
        System.out.println(s5 == s6);
        System.out.println("-----------------------------------");
        String s7 = new String("1") + new String ("8");
        String s8 = "18";
        s7.intern();
        System.out.println(s7 == s8);
    }
}

小夥伴們是不是都思考完了,下面我來揭曉答案.
噹噹噹當,JDK1.6及以前版本的答案:
在這裏插入圖片描述
JDK1.7及以後版本的答案:
在這裏插入圖片描述
這個題看起來很簡單,但是坑卻很多.要講明白這個題也需要首先明確幾個知識點:
首先是第一個天坑,這個坑就是StringTable位置的變化.
這個故事要從JDK1.7開始說起.
在JDK1.7之前,StringTable 是放在永久代的.在JDK1.7的時候,Java對StringTable的結構做出了重大的調整,將StringTable放入到了堆之中.
這也是造成上面兩個版本答案不一樣的情況.

然後我們再來說一說這個題的另外一個坑,String.intern()方法.
在這裏插入圖片描述
這是JDK源碼中對這個方法的描述.其中比較重要的兩個信息是用紅線標出來的地方.
當我們調用這個方法時,如果本來這個字符串在字符串常量池中存在(用equals比較爲true)的話,我們就返回這個字符串.
如果本來這個字符串在字符串常量池中不存在的時候,我們就把這個字符串添加到常量池中,並返回這個字符串的引用.
if and only if (當且僅當) 兩個字符串equal爲真的時候, 他們的intern比較纔會爲真.

另外一個方面,這個面試題還考察字符串拼接的底層實現.
那麼又引出了一個題:

public class TestString2 {
    public static void main(String[] args) {
        String str1 = "a";
        String str2 = "b";
        String str3 = "ab";
        System.out.println(str3 == str1 + str2);
        String str4 = "a" + "b";
        System.out.println(str3 == str4);
        String str5 = str1 + "b";
        System.out.println(str3 == str5);
        final String  str6 = "a";
        String str7 = str6 + "b";
        System.out.println(str3 == str7);
    }
}

這個題的答案小夥伴們知道麼~
在這裏插入圖片描述
怎麼樣,答對了麼~~關於字符串拼接,我總結出了一點: 只要拼接符號兩側有任何一個是變量, 那麼這個拼接出來的字符串 就不 == 與本來就在字符串常量池中的字符串.
有變量參加的字符串拼接的底層是使用StringBuilder來實現的.

        String str5 = str1 + "b";
        //首先,要new StringBuilder(),這裏我們假設這個變量是sb,那麼就是
        StringBuilder sb = new StringBuilder();
        //然後,調用append方法
        sb.append(str1);
        sb.append("b");
        //然後調用toString方法.
        //而toSring()方法是返回一個 new String(value, 0, count);
        str5 = sb.toString();        

這也就是說有了變量參加的字符串拼接最後的返回 的是一個新new出來的String對象.一會我會畫圖說明一下

而且!!!.有一個非常重要的知識點是. 當我們執行完之後,其實在常量池中,並沒有"ab" 這一個字符串.
這裏要劃重點喲~~

**

那麼讓我們回到這個面試題本身來.

**

 		String s1 = new String("1") + new String("2");
        String s2 = "12";
        System.out.println(s1 == s2);//java 1.6 和java 1.8 答案都是false
        System.out.println("-----------------------------------");

在這裏插入圖片描述
就像之前說的,S1 相當於另外new了一個對象,而對象的保存的是"12" .
通過看字節碼文件也能看出來
在這裏插入圖片描述
而,這個字節碼文件也從一方面證明了,我們上面說的結論:在執行完拼接或者說是new String(“xxx”)時,字符串常量池中並沒有生成對應的字符串.
所以,第一種情況 無論是1.6還是1.8都是false;

        String s3 = new String("1") + new String("3");
        s3.intern();
        String s4 = "13";
        System.out.println(s3 == s4);

1.6及以前的原理:
在這裏插入圖片描述
當S3.intren()時, 因爲此時字符串常量池沒有"13",所以,我們就會在常量池中添加這個字符串"13".而當S4=“13"的時候,就直接引用了常量池中的字符串,所以S3 == S4 = false;
1.7及以後:
在這裏插入圖片描述
而當字符串常量池放入到堆空間中之後,爲了節約空間,當我們使用.intern()方法時,常量池中不再時創建一個"13”,而是直接使用了之前"13"的地址值. 所以此時S3==S4是true

第三種情況:

		String s5 = new String("1") + new String("4");
	    String s6 = s5.intern();
	    System.out.println(s5 == s6);

這個基本原理就跟第二種一樣了.

第三種情況:

        String s7 = new String("1") + new String ("8");
        String s8 = "18";
        s7.intern();
        System.out.println(s7 == s8);

在這裏插入圖片描述
因爲執行順序問題. S7 = new String(“18”) 之後沒有立即執行.intern()方法,而S8這個時候直接 =“18”, 這樣就使得 常量池中就創建了常量"18".所以,無論是1.6還是1.8當中,都是false.

以上~

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章