1、什么是intern?
String::intern()是一個(gè)本地方法,它的作用是如果字符串常量池中已經(jīng)包含一個(gè)等于此String對(duì)象的字符串,則返回代表池中這個(gè)字符串的String對(duì)象的引用;否則,會(huì)將此String對(duì)象包含的字符串添加到常量池中,并且返回此String對(duì)象的引用。----書上描述
已經(jīng)包含。。。可能不太好理解。我畫圖解釋
方法的執(zhí)行順序肯定是從下向上依次執(zhí)行的。
String s1 = “11”; 先去字符串常量池中查看是否已經(jīng)存在“11”,如果存在則直接返回"11"的地址,如果不存在則會(huì)在字符串常量池中創(chuàng)建“11”,然后返回創(chuàng)建好的“11”的地址。因?yàn)橹拔磩?chuàng)建過,所以該指令會(huì)先去字符串常量池中創(chuàng)建“11”,然后返回11的地址 501。
String s2 = new String(“11”); 也是先去字符串常量池中查看是否已經(jīng)存在“11”,如果有就在堆中創(chuàng)建對(duì)象,存的是字符串常量池中“11”的地址501,最后,將堆中對(duì)象的地址601返回給S2 。
String s3 = s2.intern(); intern方法會(huì)從字符串常量池中,查詢當(dāng)前s2字符串"11"是否存在,若不存在就會(huì)在字符串常量池中創(chuàng)建字符串"11"的實(shí)例, 由于s1已經(jīng)在字符串常量池中已經(jīng)創(chuàng)建了“11”,s2.intern()則返回501。
System.out.println(s1==s2);
System.out.println(s1==s3);
false
true
s1==s2 返回false是因?yàn)閟1指向的是字符串常量池中的"11"對(duì)象(501),而s2指向的是堆中存儲(chǔ)了"11"的地址的對(duì)象(601)
s1==s3 返回true 是因?yàn)閟3和s1都指向的是字符串常量池中的"11"。
對(duì)String srt = new String(“str”)與 str1 = ”str“的區(qū)別不了解的話可以看篇文章
2、經(jīng)典例題解釋
例1
//1
String s = new String("1");
//2
s.intern();//調(diào)用此方法之前,字符串常量池中已經(jīng)存在了"1".
//3
String s2 = "1";
//4
System.out.println(s==s2);
- 執(zhí)行1,會(huì)創(chuàng)建兩個(gè)對(duì)像,在字符串常量池中創(chuàng)建一個(gè)”1“,在堆中創(chuàng)建一個(gè)String對(duì)象,并存儲(chǔ)“1”在字符串常量池中的的地址。
- 執(zhí)行2,調(diào)用此方法之前,字符串常量池中已經(jīng)存在了"1",所以此指令什么都沒做,此時(shí)s指向堆中的對(duì)象。
- 執(zhí)行3,由于字符串常量池中已經(jīng)存在了“1”,所以不需要再創(chuàng)建,返回“1”的地址,s2指向字符串常量池中的”1“。
- 執(zhí)行4,由于s指向堆中對(duì)象,s2指向字符串常量池中的對(duì)象,所以打印結(jié)果是false。
這是比較簡單的例子
下面上強(qiáng)度了!?。。。?!
例2
//1
String s3 = new String("1")+new String("1");
//2
s3.intern();
//3
String s4 = "11";
//4
System.out.println(s3==s4);
-
執(zhí)行1,會(huì)在堆中創(chuàng)建兩個(gè)存儲(chǔ)字符串”1“的地址的對(duì)象和一個(gè)字面量為”11“的String對(duì)象,在字符串常量池中創(chuàng)建一個(gè)”1“字符串實(shí)例。
在編譯期間,在創(chuàng)建String對(duì)象之前,會(huì)先創(chuàng)建一個(gè)StringBuilder對(duì)象,后面每次在堆中創(chuàng)建完一個(gè)String對(duì)象,會(huì)對(duì)該字符串執(zhí)行LDC指令,LDC指令會(huì)先到字符串常量池中查找是否存在對(duì)應(yīng)字符串實(shí)例的引用,如果有的話,那么直接返回這個(gè)字符串實(shí)例的地址給堆中的new的String對(duì)象,如果沒有的話,會(huì)創(chuàng)建一個(gè)字符串實(shí)例,然后將其添加到字符串常量池中,之后再返回這個(gè)字符串實(shí)例對(duì)象的地址給堆中創(chuàng)建的對(duì)象。完成String對(duì)象初始化后,都會(huì)執(zhí)行StringBuilder::append()方法,將該字符串拼接到StringBuilder對(duì)象里。當(dāng)都拼接完,會(huì)執(zhí)行StringBuilder::toString()方法,返回一個(gè)在堆中新new的String對(duì)象,value為"11"。
可以通過編譯后的字節(jié)碼文件清晰的觀察整個(gè)過程:
標(biāo)注的都是剛才介紹的行為。 -
執(zhí)行2,由于toString()方法只在堆中創(chuàng)建了一個(gè)”存儲(chǔ)11"的String對(duì)象,并沒有在字符串常量池中創(chuàng)建"11"字符串實(shí)例。按之前的說法intern應(yīng)該去字符串常量池中創(chuàng)建字符串”11“的實(shí)例,但是在JDK7中,并沒有在字符串常量池中創(chuàng)建字符串"11"的實(shí)例,由于堆中已經(jīng)存在了"11"這個(gè)String對(duì)象,那么為了節(jié)省空間,會(huì)在字符串常量池中申請(qǐng)一塊空間,存放這個(gè)String對(duì)象的地址,并返回存放的地址,也就是堆中存儲(chǔ)"11"的String對(duì)象的地址。
-
執(zhí)行3,會(huì)去字符串常量池中查找是否存在"11",因?yàn)榈诙幸呀?jīng)在字符串常量池中,創(chuàng)建了存儲(chǔ)堆中存儲(chǔ)"11"的String對(duì)象的地址的空間,則直接返回空間里面存儲(chǔ)的堆中String對(duì)象的地址,所以s4也指向了堆中存儲(chǔ)"11"的String對(duì)象。文章來源:http://www.zghlxwxcb.cn/news/detail-658439.html
-
由于s3和s4都指向堆中同一個(gè)對(duì)象,所以打印結(jié)果為true。
如圖所示:
通過debug,可以看到兩個(gè)句柄指向的地址是一樣的:
打印結(jié)果:文章來源地址http://www.zghlxwxcb.cn/news/detail-658439.html
例3
//1
String s3 = new String("1")+new String("1");
//2
String s4 = "11";
//3
s3.intern();
//4
System.out.println(s3==s4);
- 第一行執(zhí)行結(jié)果與例2的第一行執(zhí)行結(jié)果一致。
- 執(zhí)行2,由于字符串常量池中,還沒有"11",則向字符串常量池中創(chuàng)建字符串"11"的實(shí)例,并返回實(shí)例的地址。
- 執(zhí)行3,由于字符串常量池中已經(jīng)存在"11",則不需要再創(chuàng)建,返回實(shí)例的地址,但由于沒有賦值操作,所以該行代碼沒有什么作用。
- 此時(shí),s3指向堆中存儲(chǔ)了“11”的String對(duì)象,而s4指向的是字符串常量池中字符串為"11"的實(shí)例,所以指向的地址不同打印結(jié)果為false
通過debug可以看到兩個(gè)句柄指向的地址是不同的:
打印結(jié)果:
到了這里,關(guān)于【JVM】對(duì)String::intern()方法深入詳解(JDK7及以上)的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!