-
String
定義:
public final class String
implements java.io.Serializable, Comparable<String>, CharSequence {
可以看出 String 是 final 類型的流酬,表示該類不能被其他類繼承,同時該類實現(xiàn)了三個接口:java.io.Serializable Comparable<String> CharSequence
屬性:
private final char value[];
這是一個字符數(shù)組物延,并且是 final 類型捐友,用于存儲字符串內(nèi)容。從 final 關鍵字可以看出逆济,String 的內(nèi)容一旦被初始化后蜀肘,其不能被修改的绊汹。
所以對String的修改都是將引用指向了新的字符串。
構造方法:
String可以用byte[]扮宠,String西乖,StringBuffer,StringBuilder涵卵,char[]構造
其中用byte[]構造的時候浴栽,涉及到編碼問題,編碼用Charset指定轿偎。
其中還有一個構造方法要注意:
String(char[] value, boolean share) {
// assert share : "unshared not supported";
this.value = value;
}
這個構造方法是把char[]的引用直接給String內(nèi)部的char[],而其他構造方法則是new一個char[],值復制典鸡。這個是一個protected方法,所以不用擔心安全問題坏晦。
其他方法:
String的其他方法在api文檔直接查就可以了萝玷,這里我就不列舉了。
由于String的特性昆婿,對String有修改的方法都是返回一個新的String對象球碉。
編碼問題
之前提到的String內(nèi)部是用一個char[]來存儲的。而char是unicode編碼仓蛆,是兩個字節(jié)存一個字符睁冬。
但是用getbytes()
方法獲得的byte[]的長度并不是char[]長度*2。原因是getbytes()
方法獲得的byte[]并不是unicode編碼看疙。String類型的默認編碼方式是和本地編碼方式相關豆拨。編碼方式也可以指定。
之所以提這個是因為我之前寫的代碼需要把固定長度的String轉(zhuǎn)換為固定長度的byte[]能庆,但是GBK,UTF-8,UTF-16編碼都是不等長編碼施禾,而iso8859-1編碼不支持中文。而在charest的編碼集里沒有找到等長編碼的unicode編碼搁胆。
最后我用toCharArray()
獲得String內(nèi)部的char[]的副本弥搞,因為char是unicode編碼邮绿,所以我把char[]轉(zhuǎn)換成了char[]長度*2的byte[]數(shù)組。
String常量池與字符串拼接性能優(yōu)化
String常量池
String常量池是一個初始為空的字符串池攀例,它由類 String 私有地維護船逮。
String s1="hanhan";
String s2="hanhan";
System.out.println(s1==s2);//true
當我們創(chuàng)建String對象采用字面量形式時,JVM首先會對這個字面量進行檢查粤铭,如果常量池中存放有該字面量傻唾,則直接使用,否則創(chuàng)建新的對象并將其引用放入常量池中承耿。
String s1="gh";
String s2=new String("gh");
s2.intern();
System.out.println(s1==s2);//false;
s2=s2.intern();
System.out.println(s1==s2);//true;
當調(diào)用 intern 方法時,如果池已經(jīng)包含一個等于此 String
對象的字符串(equals(Object)
方法確定)伪煤,則返回池中的字符串加袋。否則,將此 String
對象添加到池中抱既,并返回此 String
對象的引用职烧。
常量池中存的是對對象的引用,存儲于JVM的方法區(qū)中防泵,而且引用的對象存儲于堆中蚀之。
當常量池中的引用沒有被任何變量引用時,就會被GC回收捷泞!
String常量池
String s1="a"+"b"+"c";
正常情況下足删,執(zhí)行聲明s1,代碼會生成3個對象锁右,即對象a失受、對象ab、對象abc咏瑟,其中對象a和對象ab都是中間的臨時變量拂到,最后的對象abc才賦值給了s1。因此在使用字符串拼接的時候码泞,拼接的數(shù)量越多兄旬,性能越低!
但是java編譯器在編譯的時候做了優(yōu)化余寥,在編譯時新建一個對象StringBuilder來拼接领铐,這樣就避免了產(chǎn)生很多臨時對象,從而提升了性能劈狐!
String s="";
for(int i=0;i<length;i++){
s+=i;
}
但是這個做法效率會很低罐孝。因為循環(huán)內(nèi),每次都在做字符串拼接肥缔,每次都在產(chǎn)生一個StringBuilder對象莲兢,造成內(nèi)存的浪費!因此這種錯誤要盡量避免,稍做以下優(yōu)化即可完美改造:
String s="";
StringBuilder sb=new StringBuilder();
for(int i=0;i<length;i++){
sb.append(i);
}
-
Stringbuffer與Stringbuilder
在線程安全上改艇,StringBuilder
是線程不安全的收班,而StringBuffer
是線程安全的。因為StringBuffer
的大部分方法帶有synchronized
修飾符谒兄,而StringBuilder
沒有摔桦。
StringBuilder
:適用于單線程下在字符緩沖區(qū)進行大量操作的情況
StringBuffer
:適用多線程下在字符緩沖區(qū)進行大量操作的情況
這兩個類的大多數(shù)方法都是繼承自父類AbstractStringBuilder
〕衅#或者做了少量的應用邻耕。所以要了解這兩個類,應該去了解父類AbstractStringBuilder
燕鸽。
/**
* 底層存儲的字符數(shù)組
*/
char[] value;
/**
* 用于記錄存的字符的數(shù)目
*/
int count;
從它的成員變量我們可以發(fā)現(xiàn)和ArrayList
的有些類似兄世,事實上看過源碼之后你會發(fā)現(xiàn)他們的底層機制是很類似的。
我們主要研究append()
方法啊研,以append(String str)
為例
public AbstractStringBuilder append(String str) {
if (str == null) str = "null";
int len = str.length();
ensureCapacityInternal(count + len);
str.getChars(0, len, value, count);
count += len;
return this;
}
其實就是把str的內(nèi)容復制添加到內(nèi)部的char數(shù)組value的末尾御滩。
關鍵是ensureCapacityInternal(count + len);
這行代碼。它的作用是檢查value的容量夠不夠党远,不夠的話就擴充削解,這個和ArrayList
的擴充機制基本一致,看過源碼后你會發(fā)現(xiàn)沟娱,連寫法都差不多氛驮。
int newCapacity = value.length * 2 + 2;
新的容量是舊容量的2倍+2,默認容量是16。