文章目錄
基本特性:
1、字符串常量池Jdk1.7之前位于方法區(qū),1.7開始位于堆
2、字符串常量池中同樣的數(shù)據(jù)只存儲一份(固定大小HashTable存儲數(shù)據(jù))
3、使用 -XX:StringTableSize 可設置大小,不會像HashMap一樣動態(tài)擴容,值太小造型Hash沖突嚴重,調(diào)用String.interns時性能會大幅下降
4、Jdk1.8中默認大小60013,1009是可設置最小值
字符串拼接:
1、通過StringBuilder的append()方法拼接字符串,自始至終只會創(chuàng)建一個StringBuilder的對象
2、使用String的字符串拼接,每次拼接都會創(chuàng)建一個StringBuilder和String對象,內(nèi)存占用增大,也會增加GC頻率
字符串拼接優(yōu)化:
1、理論上初始化StringBuilder對象時,指定大小,從而設的數(shù)組大小,可以提高效率(減少擴容、復制次數(shù))
2、但是,通過測試,發(fā)現(xiàn)設定大小與不設定,耗時相差無幾(毫秒)
intern方法的使用:
情況一:intern方法會從字符串常量池中查詢當前字符串是否存在,若不存在,則會將當前字符串放入常量池中并把地址返回棧中引用,存在則將地址返回給棧中引用。
String s1 = “JavaEEHadoop”; //在字符串常量池中創(chuàng)建 “JavaEEHadoop”String s2 = new String(“JavaEEHadoop”).intern(); //會將字符串常量池中 “JavaEEHadoop” 地址 返回s2System.out.println(s1 == s2); true
情況二:如果對象在堆中已經(jīng)創(chuàng)建了,字符串常量池中就不需要再創(chuàng)建新的對象了,而是直接保存堆中對象的引用,也就節(jié)省了一部分的內(nèi)存空間
下述情況適用于Jdk1.7、1.8
/*** 此代碼會在字符串常量池中 創(chuàng)建 “JavaEE”、”Hadoop“* 會使用StringBUilder來拼接,最后執(zhí)行toString方法* * 此時在堆中是存在值為 “JavaEEHadoop” 的字符串對象的*/String s1 = new String(“JavaEE”) + new String(“Hadoop”);/*** 由于s1在堆中已存在,因此為了節(jié)省空間,字符串常量池中并不會創(chuàng)建 “JavaEEHadoop”* 而是保存 堆中對象的引用*/s1.intern();/*** 由于此刻字符串常量池中已存在,因此s2會指向常量池中 堆中對象的引用*/String s2 = “JavaEEHadoop”;System.out.println(s1 == s2); true
是否引用同一份對象?
String s1 = “JavaEE”;String s2 = “Hadoop”;String s3 = “JavaEEHadoop”;/*** 編譯器優(yōu)化 為 JavaEEHadoop,s3 == s4 為true,在字符串常量池中同一份*/String s4 = “JavaEE” + “Hadoop”;System.out.println(s3 == s4); true/*** 如果拼接符號的前后出現(xiàn)了變量* 1、StringBuilder.toString中 s = new StringBuilder();* 2、s.append(“JavaEE”);* 3、s.append(“Hadoop”);* 4、s.toString(); –>類似于 new String(char[])* 但跟String s = new String(“JavaEEHadoop”)不一樣* 由于StringBuilder.toString中new String中參數(shù)是char[]數(shù)組* 因此并不會在字符串常量池中創(chuàng)建 ”JavaEEHadoop“*/String s1 = “JavaEE”; //字符串常量池中 “JavaEE”String s2 = “Hadoop”; //字符串常量池中 “Hadoop”String s3 = “JavaEEHadoop”;String s4 = s1 + “Hadoop”; String s5 = s1 + s2;System.out.println(s3 == s4); falseSystem.out.println(s3 == s5); false/*** final修飾的string變量相加時,編譯器會優(yōu)化為 ab,不會用StringBuilder拼接* s11 == s12 為true,在字符串常量池中同一份* s12會被編譯器優(yōu)化為 “ab”*/final String s1 = “a”;final String s2 = “b”;String s3 = “ab”;String s4 = s1 + s2;System.out.println(s2 == s4); true
創(chuàng)建了幾個對象?
/*** 創(chuàng)建了1個或2個* 執(zhí)行步驟,對應字節(jié)碼步驟* * 1、堆中開辟String對象空間 new #8 * 2、如果 “ab” 在字符串常量池中存在,那么久不創(chuàng)建,如果不存在則創(chuàng)建* 3、初始化String對象*/String s1 = new String(“ab”);/*** 字節(jié)碼*/步驟1、new #17 步驟1、dup步驟2、ldc #14 步驟3、invokespecial #18 /*** 如果字符串常量池中 “a”、”b”不存在,那么會創(chuàng)建6個對象* * 執(zhí)行步驟對應字節(jié)碼步驟* 1、堆中開辟StringBuilder對象空間,初始化StringBuilder對象 * 2、堆中開辟String對象空間* 3、在字符串常量池中創(chuàng)建 “a”* 4、初始化String對象* 5、執(zhí)行append方法* * 6、堆中開辟String對象空間* 7、在字符串常量池中創(chuàng)建 “b”* 8、初始化String對象* 9、執(zhí)行append方法* 10、執(zhí)行toString方法* StringBuilder對象toString方法執(zhí)行說明,由于返回的是String對象,因此會執(zhí)行toString方法* 11、堆中開辟String對象空間* 由于調(diào)用的是new String(char[])構(gòu)造方法* 因此并不會在字符串常量池中創(chuàng)建 “ab”*/String s1 = new String(“a”) + new String(“b”);/*** 字節(jié)碼文件*/步驟1、new #8 步驟1、dup步驟1、invokespecial #9 步驟2、new #17 步驟2、dup步驟3、ldc #12 步驟4、invokespecial #18 步驟5、invokevirtual #10 步驟6、new #17 步驟6、dup步驟7、ldc #13 步驟8、invokespecial #18 步驟9、invokevirtual #10 步驟10、invokevirtual #11 步驟11、new #80 步驟11、dup//我們可以看到toString方法中并沒有 在字符串常量池中創(chuàng)建 “ab”aload_0getfield #234 iconst_0aload_0getfield #233 invokespecial #291 areturn
提示:這里對文章進行總結(jié):
建議大家不要卷、不要卷、不要卷