一个简单但不简约的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.

以上~

猜你喜欢

转载自blog.csdn.net/shiliu_baba/article/details/106910089