特性
不可變類:所謂的不可變類是指這個類的實例一旦創(chuàng)建完成后,就不能改變其成員變量值堰塌。以下幾點保證了String的不可變性:
- String類被final修飾湃望,不可繼承
- 內(nèi)部所有成員都設置為私有變量且不存在value的set方法
- 獲取value時不是直接返回對象引用,而是返回對象的copy
成員變量
private final char value[];
private final int offset;
private final int count;
其中炸卑,value存放具體字符串,offset表示偏移量煤傍,count表示char的數(shù)量矾兜。同時注意到,三者均為final修飾患久,根據(jù)java中final的要求所修飾變量要么在聲明時候初始化椅寺,要么在構(gòu)造函數(shù)中初始化浑槽。
常量池
jvm虛擬機為每個被裝載的類型維護一個常量池。常量池就是該類型所用到常量的一個有序集合返帕,包括直接常量(string,integer和 floating point常量)和對其他類型桐玻,字段和方法的符號引用。
對于String常量荆萤,它的值是在常量池中的镊靴。而JVM中的常量池在內(nèi)存當中是以表的形式存在的, 對于String類型链韭,有一張固定長度CONSTANT_String_info表用來存儲文字字符串值偏竟,注意:該表只存儲文字字符串值,不存儲符號引用敞峭。常量池中保存著很多String對象; 并且可以被共享使用踊谋,因此它提高了效率。
字符串常量池優(yōu)勢:
- 節(jié)省內(nèi)存空間:常量池中所有相同的字符串常量被合并旋讹,只占用一個空間殖蚕。
- 節(jié)省運行時間:比較字符串時,==比equals()快沉迹。對于兩個引用變量睦疫,只用==判斷引用是否相等,也就可以判斷實際值是否相等鞭呕。
分析如下代碼:
// str1指向在常量池中生成一個字符串123
String str1 = "123";
// str2通過new的形式指向了堆中生成的字符串蛤育,常量池中已經(jīng)有123
String str2 = new String("123");
// str3在常量池和堆中各生成一個字符串,自身指向堆中數(shù)據(jù)
String str3 = new String("456");
常見問題
String s1 = new String(“xyz”); 創(chuàng)建了幾個對象?
- 類加載對一個類只會進行一次葫松⊥吒猓”xyz”在類加載時就已經(jīng)創(chuàng)建并駐留了(如果該類被加載之前已經(jīng)有”xyz”字符串被駐留過則不需要重復創(chuàng)建用于駐留的”xyz”實例)。駐留的字符串是放在全局共享的字符串常量池中的进宝。
- 在這段代碼后續(xù)被運行的時候,”xyz”字面量對應的String實例已經(jīng)固定了枷恕,不會再被重復創(chuàng)建党晋。所以這段代碼將常量池中的對象復制一份放到heap中,并且把heap中的這個對象的引用交給s1 持有徐块。
- 所以創(chuàng)建2個對象
String真的不能修改嗎未玻?
可以用反射完成String修改,參考如下demo:
Field valueField = String.class.getDeclaredField("value");
valueField.setAccessible(true);
char[] value = (char[]) valueField.get(s);
value[5] = '_';
java.lang.String.intern()本質(zhì)
- String的intern()方法會查找在常量池中是否存在一份equal相等的字符串,如果有則返回該字符串的引用,如果沒有則添加自己的字符串進入常量池胡控。