文章目錄
- 1、什么是intern?
- 2、經典例題解釋
- 例1
- 例2
- 例3
1、什么是intern?
String::intern()是一個本地方法,它的作用是如果字符串常量池中已經包含一個等于此String對象的字符串,則返回代表池中這個字符串的String對象的引用;否則,會將此String對象包含的字符串添加到常量池中,并且返回此String對象的引用。----書上描述
已經包含。。。可能不太好理解。我畫圖解釋
方法的執行順序肯定是從下向上依次執行的。
String s1 = “11”; 先去字符串常量池中查看是否已經存在“11”,如果存在則直接返回"11"的地址,如果不存在則會在字符串常量池中創建“11”,然后返回創建好的“11”的地址。因為之前未創建過,所以該指令會先去字符串常量池中創建“11”,然后返回11的地址 501。
String s2 = new String(“11”); 也是先去字符串常量池中查看是否已經存在“11”,如果有就在堆中創建對象,存的是字符串常量池中“11”的地址501,最后,將堆中對象的地址601返回給S2 。
String s3 = s2.intern(); intern方法會從字符串常量池中,查詢當前s2字符串"11"是否存在,若不存在就會在字符串常量池中創建字符串"11"的實例, 由于s1已經在字符串常量池中已經創建了“11”,s2.intern()則返回501。
System.out.println(s1==s2);
System.out.println(s1==s3);false
true
s1==s2 返回false是因為s1指向的是字符串常量池中的"11"對象(501),而s2指向的是堆中存儲了"11"的地址的對象(601)
s1==s3 返回true 是因為s3和s1都指向的是字符串常量池中的"11"。
對String srt = new String(“str”)與 str1 = ”str“的區別不了解的話可以看篇文章
2、經典例題解釋
例1
//1
String s = new String("1");
//2
s.intern();//調用此方法之前,字符串常量池中已經存在了"1".
//3
String s2 = "1";
//4
System.out.println(s==s2);
- 執行1,會創建兩個對像,在字符串常量池中創建一個”1“,在堆中創建一個String對象,并存儲“1”在字符串常量池中的的地址。
- 執行2,調用此方法之前,字符串常量池中已經存在了"1",所以此指令什么都沒做,此時s指向堆中的對象。
- 執行3,由于字符串常量池中已經存在了“1”,所以不需要再創建,返回“1”的地址,s2指向字符串常量池中的”1“。
- 執行4,由于s指向堆中對象,s2指向字符串常量池中的對象,所以打印結果是false。
這是比較簡單的例子
下面上強度了!!!!!!
例2
//1String s3 = new String("1")+new String("1");//2s3.intern();//3String s4 = "11";//4System.out.println(s3==s4);
-
執行1,會在堆中創建兩個存儲字符串”1“的地址的對象和一個字面量為”11“的String對象,在字符串常量池中創建一個”1“字符串實例。
在編譯期間,在創建String對象之前,會先創建一個StringBuilder對象,后面每次在堆中創建完一個String對象,會對該字符串執行LDC指令,LDC指令會先到字符串常量池中查找是否存在對應字符串實例的引用,如果有的話,那么直接返回這個字符串實例的地址給堆中的new的String對象,如果沒有的話,會創建一個字符串實例,然后將其添加到字符串常量池中,之后再返回這個字符串實例對象的地址給堆中創建的對象。完成String對象初始化后,都會執行StringBuilder::append()方法,將該字符串拼接到StringBuilder對象里。當都拼接完,會執行StringBuilder::toString()方法,返回一個在堆中新new的String對象,value為"11"。
可以通過編譯后的字節碼文件清晰的觀察整個過程:
標注的都是剛才介紹的行為。 -
執行2,由于toString()方法只在堆中創建了一個”存儲11"的String對象,并沒有在字符串常量池中創建"11"字符串實例。按之前的說法intern應該去字符串常量池中創建字符串”11“的實例,但是在JDK7中,并沒有在字符串常量池中創建字符串"11"的實例,由于堆中已經存在了"11"這個String對象,那么為了節省空間,會在字符串常量池中申請一塊空間,存放這個String對象的地址,并返回存放的地址,也就是堆中存儲"11"的String對象的地址。
-
執行3,會去字符串常量池中查找是否存在"11",因為第二行已經在字符串常量池中,創建了存儲堆中存儲"11"的String對象的地址的空間,則直接返回空間里面存儲的堆中String對象的地址,所以s4也指向了堆中存儲"11"的String對象。
-
由于s3和s4都指向堆中同一個對象,所以打印結果為true。
如圖所示:
通過debug,可以看到兩個句柄指向的地址是一樣的:
打印結果:
例3
//1String s3 = new String("1")+new String("1");//2String s4 = "11";//3s3.intern();//4System.out.println(s3==s4);
- 第一行執行結果與例2的第一行執行結果一致。
- 執行2,由于字符串常量池中,還沒有"11",則向字符串常量池中創建字符串"11"的實例,并返回實例的地址。
- 執行3,由于字符串常量池中已經存在"11",則不需要再創建,返回實例的地址,但由于沒有賦值操作,所以該行代碼沒有什么作用。
- 此時,s3指向堆中存儲了“11”的String對象,而s4指向的是字符串常量池中字符串為"11"的實例,所以指向的地址不同打印結果為false
通過debug可以看到兩個句柄指向的地址是不同的:
打印結果: