前言
Java 字符串底層是如何存儲的,如何提高性能的,今天就來好好了解一下。
字符串的存儲結構
Jvm 有專門的字符串常量池用於存放字符串,存放字符串的數據結構是HashTable。
HashTable的數據結構如下:
看個案例:
public class StringDemo { public static void main(String[] args) { String a = "11"; String b = new String("11"); System.out.println("a的HashCode:"+a.hashCode()); System.out.println("b的HashCode:"+b.hashCode()); System.out.println("a==b :"+(a==b)); // 比較的指針地址 System.out.println("a.equals(b) :"+ a.equals(b)); // 比較的是hashCode } }
運行結果:
通過案例我們來詳細說明一下,Jvm如何創建一個String字符串的。
String 字符串會創建多少個Oop(Oop 是指Java 對象在Jvm中的存在形式)?
String a = "11";
我們可以通過idea 來看創建了多少個Oop。
調式字符串賦值後。 char[] 和 String 都加一了。
說明創建了兩個Oop
char[] 對應 TypeArrayKlass
String 對應 TypeArrayOopDesc
畫圖說明字符串在Jvm 中的存在形式:
String a = new String ("11"); -- 創建了3和Oop
再來看一下String 拼接的案例
public class StringDemo2 { public static void main(String[] args) { String s1 = "1"; String s2 = "2"; String s3 = s1+s2; String s4 = "12"; System.out.println("s3 == s4: "+(s3 == s4)); } }
運行結果:
拼接字符串的實現原理用一句話完美解釋:
new StringBuilder(s1).append(s2).toString(); // toString 方法中有一個new String的邏輯。
並且拼接後的字符串是不放入常量池的。 看看toString 的源碼
public String toString() { // Create a copy, don't share the array return new String(value, 0, count); }
new String(value, 0, count) ,這種方式創建的String 不放入常量池。
那有什麼方式可以將拼接的字符串放入常量池嗎?答案是肯定的。
public class StringDemo2 { public static void main(String[] args) { String s1 = "1"; String s2 = "2"; String s3 = s1+s2; s3.intern(); // 將拼接的字符串放入常量池 String s4 = "12"; System.out.println("s3 == s4: "+(s3 == s4)); } }
intern 方法就是將拼接的字符串放入常量池。
再來看一個案例:
public class StringDemo2 { public static void main(String[] args) { final String s1 = "1"; final String s2 = "2"; String s3 = s1+s2; String s4 = "12"; System.out.println("s3 == s4: "+(s3 == s4)); } }
運行結果:
原因是s1,s2 都是final修飾,表示不會變,那麼String s3 = s1+s2; 其實也不會變,所以和 s3 = “12” 等價。
總結
String 字符串對應數據存放在字符串常量池。
拼接字符串實際就是StringBuilder 拼接。
final 修飾的情況。
intern 方法的作用是 將字符串加入常量池。