文章目錄
- 字符串常量池
- 駐留intern 方法
- String重載“+”,“+=”
- String a = "1"+"2" 創建了幾個對象?
- String a = new String ("1");創建了幾個對象?
- String s = new String("1");String s2 = "1";System.out.println(s== s2);System.out.println(s.intern()== s);System.out.println(s.intern()== s2);
- String,StringBuilder,StringBuffer區別
- 寫一個方法來判斷一個String是否是迴文(順讀和倒讀都一樣的詞)
- 爲什麼我們在使用HashMap的時候總是用String做key
- 沒事我來瞅瞅
參考文章:
美團技術團隊:深入解析String#intern
https://www.zhihu.com/question/29884421
https://javaranch.com/journal/200409/ScjpTipLine-StringsLiterally.html
字符串常量池
字符串常量池是什麼?
在HotSpot VM裏實現的string pool功能的是一個StringTable類,它是一個HashTable,默認值大小長度是1009;字符串常量由一個一個字符組成,放在了StringTable上。
- 在JDK6.0,StringTable長度是固定了1009,如果放入String Pool中的String非常多,就會造成hash衝突,導致鏈表過長,當調String#intern()時會需要到鏈表上一個一個找,從而導致性能大幅度下降
- JDK7.0,StringTable的長度可以通過參數指定:
-XX:StringTableSize=66666
字符串常量池存儲位置
- JDK6:常量池存儲在永久代中
- JDK7:常量池存儲在堆中。
字符串常量池存儲着什麼
個人認同:
字符串常量池:存放的是字符串的引用地址。
駐留intern 方法
當對象s調用intern(),首先查找字符串常量池中引用的字符串的值,看有沒有和s的值相等;如果有相等,則返回與s值相等的字符串的引用;如果找不到,則這把s的引用存入字符串常量池中,並返回。
String重載“+”,“+=”
Java對String的+操作符進行了重載。
例子:
public class Demo {
public static void main(String[] args) {
String a = "1";
String b = a + "2";
}
}
- 編譯:javac Demo.java
- 反編譯:javap -c Demo
public class basis.wei.Demo {
public basis.wei.Demo();
Code:
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":()V
4: return
public static void main(java.lang.String[]);
Code:
0: ldc #2 // String 1
2: astore_1
3: new #3 // class java/lang/StringBuilder
6: dup
7: invokespecial #4 // Method java/lang/StringBuilder."<init>":()V
10: aload_1
11: invokevirtual #5 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
14: ldc #6 // String 2
16: invokevirtual #5 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
19: invokevirtual #7 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
22: astore_2
23: return
}
編譯器自動創建StringBuilder對象, 並調用append()方法進行拼接,最後toString()生成結果。
String a = “1”+“2” 創建了幾個對象?
答案: 如果常量池沒"12",則創建1個;如果常量池有"12",則創建0個,返回該對象引用
原因: 編譯時,會自動優化,進行拼接成“12”;
證據:
public class Demo {
public static void main(String[] args) {
String s = "1"+"2";
}
}
- javac Demo.java #編譯
- javap -verbose Demo #查看 常量池
public class basis.wei.Demo
minor version: 0
major version: 52
flags: ACC_PUBLIC, ACC_SUPER
Constant pool:
#1 = Methodref #4.#13 // java/lang/Object."<init>":()V
#2 = String #14 // 12
#3 = Class #15 // basis/wei/Demo
#4 = Class #16 // java/lang/Object
#5 = Utf8 <init>
#6 = Utf8 ()V
#7 = Utf8 Code
#8 = Utf8 LineNumberTable
#9 = Utf8 main
#10 = Utf8 ([Ljava/lang/String;)V
#11 = Utf8 SourceFile
#12 = Utf8 Demo.java
#13 = NameAndType #5:#6 // "<init>":()V
#14 = Utf8 12
#15 = Utf8 basis/wei/Demo
#16 = Utf8 java/lang/Object
{
public basis.wei.Demo();
descriptor: ()V
flags: ACC_PUBLIC
Code:
stack=1, locals=1, args_size=1
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":()V
4: return
LineNumberTable:
line 7: 0
public static void main(java.lang.String[]);
descriptor: ([Ljava/lang/String;)V
flags: ACC_PUBLIC, ACC_STATIC
Code:
stack=1, locals=2, args_size=1
0: ldc #2 // String 12
2: astore_1
3: return
LineNumberTable:
line 10: 0
line 11: 3
}
編譯後只有一個“12”。
String a = new String (“1”);創建了幾個對象?
參考文章:請別再拿“String s = new String(“xyz”);創建了多少個String實例”來面試了吧
答案: 兩個,一個是字符串字面量"1"所對應的、駐留(intern)在一個全局共享的字符串常量池中的實例,另一個是通過new String(String)創建並初始化的、內容與"xyz"相同的實例
String s = new String(“1”);String s2 = “1”;System.out.println(s== s2);System.out.println(s.intern()== s);System.out.println(s.intern()== s2);
本題參考:String#intern
public static void main(String[] args) {
String s = new String("1");
String s2 = "1";
System.out.println(s== s2);
System.out.println(s.intern()== s);
System.out.println(s.intern()== s2);
}
主要考查intern的使用,以及字符串常量池的概念;
JDK8環境,答案
false
false
true
圖解
s== s2 爲false;可以很好理解,s1和s2的引用地址不同。
s.intern()== s爲false,s.intern()在String Pool中找到有“1”與s的值相等,所以返回找到的值相等的引用值,但與s的引用並不相等;所以false
第三個要先了解以下內容
在創建字符串對象時,會先到字符串常量池查到字符串引用的值,看有沒有值相等,如果有值相等,則將查到字符串的引用賦值給變量;如果查不到則在堆上創建實例,並將實例的引用存儲在String Table,並將引用賦值給變量。
String s2 = “1”; “1” 可以在String pool找到引用對象的值相等;所以直接將引用賦值給s2
所以上圖表示的s1的引用和常量池中的"1"的引用指向同一地址。so,s.intern()返回的是常量池中“1”的引用 與s1的引用相等。
如果以上理解了,在看看這題
public static void main(String[] args) {
String s = new String("1")+"1";
System.out.println(s.intern()== s);
String s2 = "11";
System.out.println(s.intern()== s2);
}
jdk 8答案
true
true
第一次s.intern() 發現String Pool中的引用的值沒有和11相等的,就將“11”的引用駐留在了String Pool;並將引用返回。s.intern()== s ;等於true。
第二個true;直接將“11”的引用返回給s2,如圖。
String,StringBuilder,StringBuffer區別
String | StringBuilder | StringBuffer | |
---|---|---|---|
儲存結構 | private final char value[]; | char[] value; | private transient char[] toStringCache; |
線程是安全性 | 安全 | 不安全 | 安全,synchronized修飾方法 |
運算 | 1. 編譯器優化 2. “+”,“+=” 重載 | 效率高 | 效率低 |
使用場景 | 操作少量數據 | 單線程,操作大量數據 | 多線程,操作大量數據 |
寫一個方法來判斷一個String是否是迴文(順讀和倒讀都一樣的詞)
/**
* 判斷是否是迴文
* @param s
* @return
*/
public static boolean isPalindromeString(String s){
StringBuilder stringBuilder = new StringBuilder(s);
return stringBuilder.reverse().toString().equals(s);
}
爲什麼我們在使用HashMap的時候總是用String做key
我們知道HashMap的key只能是引用類型,而在進行路由尋址時,是根據key的hashcode來計算的。
而String有以下特點。
- String 有個hash屬性,用來儲存hashcode,可避免重複計算hashCode。
- String的值是根據值來計算的,所以String 作爲key,如果他的值相等,那麼hashcode也一定相等;這樣的能減少很多實際工作中可能會出現的麻煩。
參考文章:爲什麼 HashMap 常用 String 對象作 key
//緩存hashcode
/** Cache the hash code for the string */
private int hash; // Default to 0
// 根據值計算hashcode
public int hashCode() {
int h = hash;
if (h == 0 && value.length > 0) {
char val[] = value;
for (int i = 0; i < value.length; i++) {
h = 31 * h + val[i];
}
hash = h;
}
return h;
}
沒事我來瞅瞅
如果有什麼地方不對,麻煩請指出!謝謝!!