眾所周知,JDK1.8版本中,String常量池已經從方法區中的運行時常量池分離到堆中了,那么在堆中的String常量池里存的是String對象還是引用呢?直接查看API:
翻譯:String類的intern()方法:一個初始為空的字符串池,它由類String獨自維護。當調用 intern方法時,如果池已經包含一個等於此String對象的字符串(用equals(oject)方法確定),則返回池中的字符串。否則,將此String對象添加到池中,並返回此String對象(注意是常量池中的對象,不是堆中的對象)的引用。 對於任意兩個字符串s和t,當且僅當s.equals(t)為true時,s.intern() == t.intern()才為true。所有字面值字符串和字符串賦值常量表達式都使用 intern方法進行操作。
總結
JDK1.8版本的字符串常量池中存的是字符串對象,以及字符串常量值。
附上常考面試題:
- 輸出結果?創建了幾個對象?
String s1 = "abc";
String s2 = "abc";
System.out.println(s1==s2);
結果輸出:
true
創建了1個對象。采用字面值的方式創建一個字符串時,JVM首先會去字符串池中查找是否存在"abc"這個對象,如果不存在,則在字符串池中創建"abc"這個對象,然后將池中"abc"這個對象的引用地址返回給"abc"對象的引用s1,這樣s1會指向池中"abc"這個字符串對象;如果存在,則不創建任何對象,直接將池中"abc"這個對象的地址返回,賦給引用s2。因為s1、s2都是指向同一個字符串池中的"abc"對象,所以結果為true。
- 輸出結果?創建了幾個對象?
String s3 = new String("xyz");
String s4 = new String("xyz");
System.out.println(s3==s4);
結果輸出:
false
創建了3個對象。采用new關鍵字新建一個字符串對象時,JVM首先在字符串池中查找有沒有"xyz"這個字符串對象,如果有,則不在池中再去創建"xyz"這個對象了,直接在堆中創建一個"xyz"字符串對象,然后將堆中的這個"xyz"對象的地址返回賦給引用s3,這樣,s3就指向了堆中創建的這個"xyz"字符串對象;如果沒有,則首先在字符串池中創建一個"xyz"字符串對象,然后再在堆中創建一個"xyz"字符串對象,然后將堆中這個"xyz"字符串對象的地址返回賦給s3引用,這樣,s3指向了堆中創建的這個"xyz"字符串對象。s4則指向了堆中創建的另一個"xyz"字符串對象。s3 、s4是兩個指向不同對象的引用,結果當然是false。
- 代碼詳解
String s1 = new StringBuilder().append("ja").append("va1").toString();
System.out.println(s1.intern() == s1);
輸出結果:
true
詳解:StringBuilder().toString()這個方法雖然是new
了一個String
對象,但其實和"java1"是一樣的,大家可以看下源碼,這里的s1.intern()
返回的是常量池中字符串的引用,所以s1.intern() == s1
。