可以證明,字符串操作是計算機程序設計中最常見的行為。
首先我們要明確,String 并不是基本數(shù)據(jù)類型,而是一個對象,并且是不可變的對象。查看源碼就會發(fā)現(xiàn) String 類為 final 型的(當然也不可被繼承),而且通過查看 JDK 文檔會發(fā)現(xiàn)幾乎每一個修改 String 對象的操作,實際上都是創(chuàng)建了一個全新的 String 對象。
字符串為對象,那么在初始化之前,它的值為 null,到這里就有必要提下 ””、null、new String() 三者的區(qū)別。null 表示 string 還沒有 new ,也就是說對象的引用還沒有創(chuàng)建,也沒有分配內存空間給他,而””、new String() 則說明了已經(jīng) new 了,只不過內部為空,但是它創(chuàng)建了對象的引用,是需要分配內存空間的。打個比方:一個空玻璃杯,你不能說它里面什么都沒有,因為里面有空氣,當然也可以把它弄成真空,null與” “、new String() 的區(qū)別就象真空與空氣一樣。
在字符串中存在一個非常特殊的地方,那就是字符串池。每當我們創(chuàng)建一個字符串對象時,首先就會檢查字符串池中是否存在面值相等的字符串,如果有,則不再創(chuàng)建,直接放回字符串池中對該對象的引用,若沒有則創(chuàng)建然后放入到字符串池中并且返回新建對象的引用。這個機制是非常有用的,因為可以提高效率,減少了內存空間的占用。所以在使用字符串的過程中,推薦使用直接賦值(即String s=”aa”),除非有必要才會新建一個 String 對象(即String s = new String(”aa”))。
對于字符串的使用無非就是這幾個方面:
1、字符串比較
equals() ——判斷內容是否相同。
compareTo() ——判斷字符串的大小關系。
compareToIgnoreCase(String int) ——在比較時忽略字母大小寫。
== ——判斷內容與地址是否相同。
equalsIgnoreCase() ——忽略大小寫的情況下判斷內容是否相同。
reagionMatches() ——對字符串中的部分內容是否相同進行比較(詳情請參考API)。
2、字符串查找
charAt(int index) ——返回指定索引 index 位置上的字符,索引范圍從 0 開始。
indexOf(String str)——從字符串開始檢索str,并返回第一次出現(xiàn)的位置,未出現(xiàn)返回 -1。
indexOf(String str,int fromIndex);——從字符串的第 fromIndex 個字符開始檢索 str。
lastIndexOf(String str)——查找最后一次出現(xiàn)的位置。
lastIndexOf(String str,int fromIndex)—-從字符串的第fromIndex個字符查找最后一次出現(xiàn)的位置。
starWith(String prefix,int toffset)—–測試此字符串從指定索引開始的子字符串是否以指定前綴開始。
starWith(String prefix)——測試此字符串是否以指定的前綴開始。
endsWith(String suffix)——測試此字符串是否以指定的后綴結束。
3、字符串截取
public String subString(int beginIndex)——返回一個新的字符串,它是此字符串的一個子字符串。
public String subString(int beginIndex,int endIndex)——返回的字符串是從 beginIndex 開始到 endIndex-1 的串。
4、字符串替換
public String replace(char oldChar,char newChar)。
public String replace(CharSequence target,CharSequence replacement)——把原來的 etarget 子序列替換為 replacement 序列,返回新串。
public String replaceAll(String regex,String replacement)——用正則表達式實現(xiàn)對字符串的匹配。注意 replaceAll 第一個參數(shù)為正則表達式,鄙人曾經(jīng)深受其害。
5、更多方法請參考 API
StringBuffer 和 String 一樣都是用來存儲字符串的,只不過由于他們內部的實現(xiàn)方式不同,導致他們所使用的范圍不同,對于 StringBuffer 而言,他在處理字符串時,若是對其進行修改操作,它并不會產(chǎn)生一個新的字符串對象,所以說在內存使用方面它是優(yōu)于 String 的。
其實在使用方法,StringBuffer 的許多方法和 String 類都差不多,所表示的功能幾乎一模一樣,只不過在修改時 StringBuffer 都是修改自身,而 String 類則是產(chǎn)生一個新的對象,這是他們之間最大的區(qū)別。
同時 StringBuffer 是不能使用=進行初始化的,它必須要產(chǎn)生 StringBuffer 實例,也就是說你必須通過它的構造方法進行初始化。
在 StringBuffer 的使用方面,它更加側重于對字符串的變化,例如追加、修改、刪除,相對應的方法:
1、append():追加指定內容到當前 StringBuffer 對象的末尾,類似于字符串的連接,這里 StringBuffer 對象的內容會發(fā)生改變。
2、insert:該類方法主要是在 StringBuffer 對象中插入內容。
3、delete:該類方法主要用于移除 StringBuffer 對象中的內容。
StringBuilder 也是一個可變的字符串對象,他與 StringBuffer 不同之處就在于它是線程不安全的,基于這點,它的速度一般都比 StringBuffer 快。與 StringBuffer 一樣,StringBuider 的主要操作也是 append 與 insert 方法。這兩個方法都能有效地將給定的數(shù)據(jù)轉換成字符串,然后將該字符串的字符添加或插入到字符串生成器中。
上面只是簡單的介紹了 String、StringBuffer、StringBuilder,其實對于這三者我們應該更加側重于他們只見到的區(qū)別,只有理清楚他們之間的區(qū)別才能夠更好的使用他們。
我們先看如下表格:
這里對于 String 是否為線程安全,鄙人也不是很清楚,原因:String 不可變,所有的操作都是不可能改變其值的,是否存在線程安全一說還真不好說?但是如果硬要說線程是否安全的話,因為內容不可變,永遠都是安全的。
在使用方面由于 String 每次修改都需要產(chǎn)生一個新的對象,所以對于經(jīng)常需要改變內容的字符串最好選擇 StringBuffer 或者 StringBuilder.而對于 StringBuffer,每次操作都是對 StringBuffer 對象本身,它不會生成新的對象,所以 StringBuffer 特別適用于字符串內容經(jīng)常改變的情況下。
但是并不是所有的 String 字符串操作都會比 StringBuffer 慢,在某些特殊的情況下,String 字符串的拼接會被 JVM 解析成 StringBuilder 對象拼接,在這種情況下 String 的速度比 StringBuffer 的速度快。如:
String name = ”I ” + ”am ” + ”chenssy ” ;
StringBuffer name = new StringBuffer(”I ”).append(” am ”).append(” chenssy ”);
對于這兩種方式,你會發(fā)現(xiàn)第一種比第二種快太多了,在這里 StringBuffer 的優(yōu)勢蕩然無存。其真實的原因就在于JVM做了一下優(yōu)化處理,其實 String name = ”I ” + ”am ” + ”chenssy ” ;在 JVM 眼中就是 String name = ”I am chenssy ” ;這樣的方式對于 JVM 而言,真的是不要什么時間。但是如果我們在這個其中增加一個 String 對象,那么 JVM 就會按照原來那種規(guī)范來構建 String 對象了。
對于這三者使用的場景做如下概括(參考:《編寫搞質量代碼:改善 Java 程序的 151 個建議》):
1、String:在字符串不經(jīng)常變化的場景中可以使用 String 類,如:常量的聲明、少量的變量運算等。
2、StringBuffer:在頻繁進行字符串的運算(拼接、替換、刪除等),并且運行在多線程的環(huán)境中,則可以考慮使用 StringBuffer,例如 XML 解析、 HTTP 參數(shù)解析和封裝等。
3、StringBuilder:在頻繁進行字符串的運算(拼接、替換、刪除等),并且運行在多線程的環(huán)境中,則可以考慮使用 StringBuffer,如 SQL 語句的拼裝、JSON 封裝等(貌似這兩個我也是使用|StringBuffer)。
更多有關于他們之間區(qū)別,請參考:http://www.cnblogs.com/zuoxiaolong/p/lang1.html 。鄙人就不畫蛇添足了。
對于字符串而言我們經(jīng)常是要對其進行拼裝處理的,在 Java 中提高了三種拼裝的方法:+、concat() 以及 append() 方法。這三者之間存在什么區(qū)別呢?先看如下示例:
public class StringTest {
/**
* @desc 使用+、concat()、append()方法循環(huán)10W次
* @author chenssy
* @data 2013-11-16
* @param args
* @return void
*/
public static void main(String[] args) {
//+
long start_01 = System.currentTimeMillis();
String a = "a";
for(int i = 0 ; i < 100000 ; i++){
a += "b";
}
long end_01 = System.currentTimeMillis();
System.out.println(" + 所消耗的時間:" + (end_01 - start_01) + "毫米");
//concat()
long start_02 = System.currentTimeMillis();
String c = "c";
for(int i = 0 ; i < 100000 ; i++){
c = c.concat("d");
}
long end_02 = System.currentTimeMillis();
System.out.println("concat所消耗的時間:" + (end_02 - start_02) + "毫米");
//append
long start_03 = System.currentTimeMillis();
StringBuffer e = new StringBuffer("e");
for(int i = 0 ; i < 100000 ; i++){
e.append("d");
}
long end_03 = System.currentTimeMillis();
System.out.println("append所消耗的時間:" + (end_03 - start_03) + "毫米");
}
}
------------
Output:
+ 所消耗的時間:19080毫米
concat所消耗的時間:9089毫米
append所消耗的時間:10毫米
從上面的運行結果可以看出,append()速度最快,concat()次之,+最慢。原因請看下面分解:
(一)+方式拼接字符串
在前面我們知道編譯器對+進行了優(yōu)化,它是使用 StringBuilder 的 append() 方法來進行處理的,我們知道 StringBuilder 的速度比 StringBuffer 的速度更加快,但是為何運行速度還是那樣呢?主要是因為編譯器使用 append() 方法追加后要同 toString() 轉換成 String 字符串,也就說 str +=”b”等同于
str = new StringBuilder(str).append(“b”).toString();
它變慢的關鍵原因就在于 new StringBuilder() 和 toString(),這里可是創(chuàng)建了 10 W 個 StringBuilder 對象,而且每次還需要將其轉換成 String,速度能不慢么?
(二)concat() 方法拼接字符串
public String concat(String str) {
int otherLen = str.length();
if (otherLen == 0) {
return this;
}
char buf[] = new char[count + otherLen];
getChars(0, count, buf, 0);
str.getChars(0, otherLen, buf, count);
return new String(0, count + otherLen, buf);
}
這是 concat() 的源碼,它看上去就是一個數(shù)字拷貝形式,我們知道數(shù)組的處理速度是非??斓?,但是由于該方法最后是這樣的:return new String(0, count + otherLen, buf);這同樣也創(chuàng)建了 10 W 個字符串對象,這是它變慢的根本原因。
(三)append() 方法拼接字符串
public synchronized StringBuffer append(String str) {
super.append(str);
return this;
}
StringBuffer 的 append() 方法是直接使用父類 AbstractStringBuilder 的 append() 方法,該方法的源碼如下:
public AbstractStringBuilder append(String str) {
if (str == null) str = "null";
int len = str.length();
if (len == 0) return this;
int newCount = count + len;
if (newCount > value.length)
expandCapacity(newCount);
str.getChars(0, len, value, count);
count = newCount;
return this;
}
與 concat() 方法相似,它也是進行字符數(shù)組處理的,加長,然后拷貝,但是請注意它最后是返回并沒有返回一個新串,而是返回本身,也就說這這個 10 W 次的循環(huán)過程中,它并沒有產(chǎn)生新的字符串對象。
通過上面的分析,我們需要在合適的場所選擇合適的字符串拼接方式,但是并不一定就要選擇 append() 和 concat() 方法,原因在于+根據(jù)符合我們的編程習慣,只有到了使用 append() 和 concat() 方法確實是可以對我們系統(tǒng)的效率起到比較大的幫助,才會考慮,同時鄙人也真的沒有怎么用過 concat() 方法。
鞏固基礎,提高技術,不懼困難,攀登高峰!?。。。。?/code>