字符串常量池StringTable 你真的了解么
本篇目錄
- 1、 String的基本特性
- 2、 String的內存分配
- 3、 字符串拼接操作
- 3.1 字符串拼接
- 3.2 拼接操作與append的效率對比
- 4、 intern()的使用
- 4.1 new String("ab")會創建幾個對象,new String("a")+new String("b")呢
- 4.2 關于String.intern()的問題
- 4.3 總結String的intern()的使用
1、 String的基本特性
? String:字符串,使用一對""引起來表示。
? String sl = "hello";//字面量的定義方式 ? String s2 = new String("hello") ;? String聲明為final的, 不可被繼承
? String實現了Serializable接口:表示字符串是支持序列化的。 實現了Comparable接口:表示String可以比較大小
? String在jdk8及以前內部定義了final char[] value用于存儲字符串數據。jdk9時改為byte[]
結論: String再也不用char[] 來存儲啦,改成了byte[] 加上編碼標記,節約了一些空間。StringBuffer和StringBuilder也做了一些修改。
? String:代表不可變的字符序列。簡稱:不可變性。
- 當對字符串重新賦值時,需要重寫指定內存區域賦值,不能使用原有的value進行賦值。
- 當對現有的字符串進行連接操作時,也需要重新指定內存區域賦值,不能使用原有的value進行賦值。
- 當調用String的replace()方法修改指定字符或字符串時,也需要重新指定內存區域賦值,不能使用原有的value進行賦值。
? 通過字面量的方式(區別于new)給一個字符串賦值,此時的字符串值聲明在字符串常量池中。
/*** String的基本使用:體現String的不可變性*/ public class StringTest1 {@Testpublic void test1() {String s1 = "abc";//字面量定義的方式,"abc"存儲在字符串常量池中String s2 = "abc";s1 = "hello";System.out.println(s1 == s2);//判斷地址:true System.out.println(s1);// helloSystem.out.println(s2);//abc}@Testpublic void test2() {String s1 = "abc";String s2 = "abc";s2 += "def";System.out.println(s2);//abcdefSystem.out.println(s1);//abc}@Testpublic void test3() {String s1 = "abc";String s2 = s1.replace('a', 'm');System.out.println(s1);//abcSystem.out.println(s2);//mbc} }? 字符串常量池中是不會存儲相同內容的字符串的。
- String的String Pool 是一個固定大小的Hashtable,默認值大小長度是1009。如果放進StringPool的String非常多, 就會造成Hash沖突嚴重,從而導致鏈表會很長,而鏈表長了后直接會造成的影響就是當調用String. intern時性能會大幅下降。
- 使用一XX: StringTableSize可設置StringTable的長度
- 在jdk6中StringTable是固定的,就是1009的長度,所以如果常量池中的字符串過多就會導致效率下降很快。StringTableSize設 置沒有要求
- 在jdk7中,StringTable的長度默認值是60013
- jdk8開始,1009是StringTable長度可設置的最小值
2、 String的內存分配
-
在Java語言中有8種基本數據類型和一種比較特殊的類型String。這些 類型為了使它們在運行過程中速度更快、更節省內存,都提供了一種常量池的概念。
-
常量池就類似一.個Java系統級別提供的緩存。8種基本數據類型的常量 池都是系統協調的,String類 型的常量池比較特殊。它的主要使用方法有兩種。
- 直接使用雙引號聲明出來的String對象會直接存儲在常量池中。 比如: String info = “abc” ;
- 如果不是用雙引號聲明的String對象,可以使用String提供的intern()方法。這個后面重點聊。
-
Java 6及以前,字符串常量池存放在永久代。
-
Java 7中Oracle的工程師對字符串池的邏輯做了很大的改變,即將字符串常量池的位置調整到Java堆內。
- 所有的字符串都保存在堆(Heap)中,和其他普通對象一樣,這樣可以讓你在進行調優應用時僅需要調整堆大小就可以了。
- 字符串常量池概念原本使用得比較多,但是這個改動使得我們有足夠的理由讓我們重新考慮在Java 7中使用String. intern()。
-
Java8元空間,字符串常量在堆
StringTable為什么要調整?
①永久代permSize默認比較小;
②永久代的垃圾回收頻率低;
3、 字符串拼接操作
3.1 字符串拼接
@Testpublic void test3(){String s1 = "a";String s2 = "b";String s3 = "ab";/*如下的s1 + s2 的執行細節:(變量s是我臨時定義的)① StringBuilder s = new StringBuilder();② s.append("a")③ s.append("b")④ s.toString() --> 約等于 new String("ab")補充:在jdk5.0之后使用的是StringBuilder,在jdk5.0之前使用的是StringBuffer*/String s4 = s1 + s2;//System.out.println(s3 == s4);//false}/*1. 字符串拼接操作不一定使用的是StringBuilder!如果拼接符號左右兩邊都是字符串常量或常量引用,則仍然使用編譯期優化,即非StringBuilder的方式。2. 針對于final修飾類、方法、基本數據類型、引用數據類型變量的結構時,能使用上final的時候建議使用上。*/@Testpublic void test4(){final String s1 = "a";final String s2 = "b";String s3 = "ab";String s4 = s1 + s2;System.out.println(s3 == s4);//true}@Testpublic void test5(){String s1 = "javaEEhadoop";String s2 = "javaEE";String s3 = s2 + "hadoop";System.out.println(s1 == s3);//falsefinal String s4 = "javaEE";//s4:常量String s5 = s4 + "hadoop";System.out.println(s1 == s5);//true}3.2 拼接操作與append的效率對比
append效率要比字符串拼接高很多!
/*體會執行效率:通過StringBuilder的append()的方式添加字符串的效率要遠高于使用String的字符串拼接方式!詳情:1、 StringBuilder的append()的方式:自始至終中只創建過一個StringBuilder的對象使用String的字符串拼接方式:創建過多個StringBuilder和String的對象2、 使用String的字符串拼接方式:內存中由于創建了較多的StringBuilder和String的對象,內存占用更大;如果進行GC,需要花費額外的時間。改進的空間:在實際開發中,如果基本確定要前前后后添加的字符串長度不高于某個限定值highLevel的情況下,建議使用構造器實例化:StringBuilder s = new StringBuilder(highLevel);//new char[highLevel]*/@Testpublic void test6(){long start = System.currentTimeMillis(); // method1(100000);//4014method2(100000);//7long end = System.currentTimeMillis();System.out.println("花費的時間為:" + (end - start));}public void method1(int highLevel){String src = "";for(int i = 0;i < highLevel;i++){src = src + "a";//每次循環都會創建一個StringBuilder、String} // System.out.println(src);}public void method2(int highLevel){//只需要創建一個StringBuilderStringBuilder src = new StringBuilder();for (int i = 0; i < highLevel; i++) {src.append("a");} // System.out.println(src);}4、 intern()的使用
如果不是用雙引號聲明的String對象,可以使用String提供的intern方法: intern方法會從字符串常量池中查詢當前字符串是否存在,若不存在就會將當前字符串放入常量池中。
比如:
也就是說,如果在任意字符串上調用String. intern方法,那么其返回結果所指向的那個類實例,必須和直接以常量形式出現的字符串實例完全相同。因此,下 列表達式的值必定是true:
("a" + "b" + "c").intern()== "abc";通俗點講,Intern() String就是確保字符串在內存里只有一份拷貝,這樣可以節約內存空間,加快字符串操作任務的執行速度。注意,這個值會被存放在字符串內部池(String Intern Pool)。
4.1 new String(“ab”)會創建幾個對象,new String(“a”)+new String(“b”)呢
public class StringNewTest {public static void main(String[] args) { // String str = new String("ab");String str = new String("a") + new String("b");} }-
new String(“ab”)會創建幾個對象?看字節碼,就知道是兩個。
- 一個對象是:new關鍵字在堆空間創建的
- 另一個對象是:字符串常量池中的對象"ab"。 字節碼指令:ldc
-
new String(“a”) + new String(“b”)呢?
- 對象1:new StringBuilder()
- 對象2: new String(“a”)
- 對象3: 常量池中的"a"
- 對象4: new String(“b”)
- 對象5: 常量池中的"b"
- 4.22
4.2 關于String.intern()的問題
/*** 如何保證變量s指向的是字符串常量池中的數據呢?* 有兩種方式:* 方式一: String s = "shkstart";//字面量定義的方式* 方式二: 調用intern()* String s = new String("shkstart").intern();* String s = new StringBuilder("shkstart").toString().intern();**/ public class StringIntern {public static void main(String[] args) {String s = new String("1");String s1 = s.intern();//調用此方法之前,字符串常量池中已經存在了"1"String s2 = "1";//s 指向堆空間"1"的內存地址//s1 指向字符串常量池中"1"的內存地址//s2 指向字符串常量池已存在的"1"的內存地址 所以 s1==s2System.out.println(s == s2);//jdk6:false jdk7/8:falseSystem.out.println(s1 == s2);//jdk6: true jdk7/8:trueSystem.out.println(System.identityHashCode(s));//491044090System.out.println(System.identityHashCode(s1));//644117698System.out.println(System.identityHashCode(s2));//644117698//s3變量記錄的地址為:new String("11")String s3 = new String("1") + new String("1");//執行完上一行代碼以后,字符串常量池中,是否存在"11"呢?答案:不存在!!//在字符串常量池中生成"11"。如何理解:jdk6:創建了一個新的對象"11",也就有新的地址。// jdk7:此時常量中并沒有創建"11",而是創建一個指向堆空間中new String("11")的地址s3.intern();//s4變量記錄的地址:使用的是上一行代碼代碼執行時,在常量池中生成的"11"的地址String s4 = "11";System.out.println(s3 == s4);//jdk6:false jdk7/8:true} }
4.3 總結String的intern()的使用
- jdk1.6中,將這個字符串對象嘗試放入串池。
- ?如果字符串常量池中有,則并不會放入。返回已有的串池中的對象的地址
- ?如果沒有,會把此對象復制一份,放入串池,并返回串池中的對象地址
- Jdk1.7起,將這個字符串對象嘗試放入串池。
- ?如果字符串常量池中有,則并不會放入。返回已有的串池中的對象的地址
- ?如果沒有,則會把對象的引用地址復制一份,放入串池,并返回串池中的引用地址
總結
以上是生活随笔為你收集整理的字符串常量池StringTable 你真的了解么的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 垃圾回收③---垃圾回收器
- 下一篇: 公平