String 是對象,默認值 null。但它是特殊對象,擁有其他對象沒有的特性(如字面量語法、常量池、不可變性)。
new String() // 空串 ""
new String("") // 空串 ""
String s = null // null 引用
常量池:編譯期確定、存於 .class 文件,包含類、方法、接口常量及字符串常量。JVM 保證字符串常量在常量池中只有一份拷貝。
String s0 = "kvill";
String s1 = "kvill";
String s2 = "kv" + "ill"; // 編譯期常量折疊
s0 == s1 // true
s0 == s2 // true
三者皆指向常量池同一個 "kvill"。
String s0 = "kvill";
String s1 = new String("kvill");
String s2 = "kv" + new String("ill");
s0 == s1 // false
s0 == s2 // false
s1 == s2 // false
new String() 在運行期於堆上創建新對象,不進入常量池,各自擁有獨立地址。
intern() 將運行期字符串放入常量池:
String s0 = "kvill";
String s1 = new String("kvill");
String s2 = new String("kvill");
s0 == s1 // false
s1.intern(); // 返回池中引用,但未賦值給 s1
s0 == s1 // false
s0 == s1.intern() // true
s2 = s2.intern(); // 將池中引用賦給 s2
s0 == s2 // true
String s1 = new String("kvill"); // 類中無 "kvill" 字面量
String s2 = s1.intern(); // 池中新增 "kvill",s1 仍指向堆對象
s1 == s1.intern() // false,證明原堆對象未被替換
s2 == s1.intern() // true,s2 指向池中引用
結論:intern() 是在池中創建/查找,而非「註冊自身地址」。
equals():比較 Unicode 字符序列是否相等==:比較引用地址是否相同(是否同一對象)String str = "kv" + "ill" + " " + "ans";
編譯期常量折疊優化為單一常量,但若涉及變量:
String a = "kv";
String b = "ill";
String str = a + b; // 運行期創建 StringBuilder -> toString(),產生臨時對象
頻繁拼接建議用 StringBuffer(線程安全)或 StringBuilder(非線程安全、性能更優),它們是可變字符序列。
| 場景 | 存儲位置 | == 比較結果 |
|---|---|---|
| 字面量 / 常量折疊 | 常量池 | true(同一引用) |
| new String() | 堆 | false(不同對象) |
| intern() 後 | 常量池 | 與池中引用相同 |