Java中的字符串處理主要有下面三個類來處理的:String、StringBuffer、StringBuilder。 html
1、String java
一、String簡介 面試
初始化: 正則表達式
通常由String聲明的字符串,長度是不可變的,這也是它與StringBuffer和StringBuilder最直觀的一個區別。通常初始化方式:String s = "hello world";通過這條語句,JVM的棧內存中產生一個s變量,堆內存中產生hello world字符串對象。s指向了hello world的地址。像上面這種方式產生的字符串屬於直接量字符串對象,JVM在處理這類字符串的時候,會進行緩存,產生時放入字符串池,當程序須要再次使用的時候,無需從新建立一個新的字符串,而是直接指向已存在的字符串。看下面程序: 算法
package com.xtfggef.string; public class StringTest4 { public static void main(String[] args) { String s = "hello world"; String s2 = "hello world"; System.out.println(s == s2); } }
該程序輸出:true 由於s和s2都指向了hello world字符串,他們的地址是同一個。 咱們常說,String的一個很大的特色,就是它是一個「不可變的字符串」,就是說,當一個String對象完成建立後,該對象的內容就固定下來了,可是爲何還會有下面這種狀況呢? express
package com.xtfggef.string; public class StringInit { public static void main(String[] args) { String str = "I like";//---------1-------- System.out.println(System.identityHashCode(str)); str = str + "java";//--------2--------- System.out.println(System.identityHashCode(str)); } }
該程序輸出: 編程
14576877
12677476 數組
說明:str彷佛是變了,這是爲何呢?實際上是這樣的:str只是一個引用變量,當程序執行完1後,str指向「I like」。當程序執行完2以後,鏈接運算符會將兩個字符串連在一塊兒,而且讓str指向新的串:"I like java",因此,從這裏應該能夠看得出來,最初的對象確實沒有改變,只是str所指向的對象在不斷改變。 緩存
String對象的另外一種初始化方式,就是採用String類提供的構造方法進行初始化。String類提供了16種構造方法,經常使用的有五種: 安全
String() --------- 初始化一個String對象,表示一個空字符序列
String(String value) --------- 利用一個直接量建立一個新串
String(char[] value) --------- 利用一個字符數組建立
String(char[] value,int offset,int count) --------- 截取字符數組,從offset開始count個字符建立
String(StringBuffer buffer) --------- 利用StringBuffer建立
形如:
String s = new String();
String s1 = new String(「hello」);
char[] c = {'h','e','l','l','o'};
String s2 = new String(c);
'String s3 = new String(c,1,3);
以上就是String類的基本初始化方法。
二、String類的一些經常使用方法
字符串是最經常使用的對象,因此,咱們有必要完全的瞭解下它,下面我會列舉經常使用的字符串裏的方法,由於有不少,就不一一列舉。
-------public int length()--------
該方法用於獲取字符串的長度,實現以下:
/** * Returns the length of this string. * The length is equal to the number of <a href="Character.html#unicode">Unicode * code units</a> in the string. * * @return the length of the sequence of characters represented by this * object. */ public int length() { return count; }
這是JDK種的原始實現,count在String類裏被定義爲一個整型常量:private final int count;而且不論採用哪一種構造方法,最終都會爲count賦值。
使用方法:
String s = "hello world"; int length = s.length();
這個比較簡單。
-----------public boolean equals(Object anObject)-----------
該方法用於比較給定對象是否與String相等。
JDK裏是這樣實現的:
/** * Compares this string to the specified object. The result is {@code * true} if and only if the argument is not {@code null} and is a {@code * String} object that represents the same sequence of characters as this * object. * * @param anObject * The object to compare this {@code String} against * * @return {@code true} if the given object represents a {@code String} * equivalent to this string, {@code false} otherwise * * @see #compareTo(String) * @see #equalsIgnoreCase(String) */ public boolean equals(Object anObject) { if (this == anObject) { return true; } if (anObject instanceof String) { String anotherString = (String)anObject; int n = count; if (n == anotherString.count) { char v1[] = value; //---------1--------- char v2[] = anotherString.value;//-------2---------- int i = offset; int j = anotherString.offset; while (n-- != 0) { if (v1[i++] != v2[j++]) return false; } return true; } } return false; }
從1和2處也看出來,String的底層是基於字符數組的。咱們能夠像下面這種方式使用equals():
String s1 = new String("hello world"); String s2 = new String("hello world"); String s3 = new String("hello"); System.out.println(s1.equals(s2));; System.out.println(s1.equals(s3));
結果輸出:
true
false
此處插入一個很重要的知識點,重寫equals()的通常步驟及注意事項:
1. 使用==操做符檢查「實參是否爲指向對象的一個引用」。
2. 使用instanceof操做符檢查「實參是否爲正確的類型」。
3. 把實參轉換到正確的類型。
4. 對於該類中每個「關鍵」域,檢查實參中的域與當前對象中對應的域值是否匹配。
a.對於既不是float也不是double類型的基本類型的域,可使用==操做符進行比較
b.對於對象引用類型的域,能夠遞歸地調用所引用的對象的equals方法
c.對於float類型的域,先使用Float.floatToIntBits轉換成int類型的值,而後使用==操做符比較int類型的值
d.對於double類型的域,先使用Double.doubleToLongBits轉換成long類型的值,而後使用==操做符比較long類型的值。
5. 當你編寫完成了equals方法以後,應該問本身三個問題:它是不是對稱的、傳遞的、一致的?(其餘兩個特性一般會自行知足) 若是答案是否認的,那麼請找到這些特性未能知足的緣由,再修改equals方法的代碼。
稍後我再作說明,請先再看個例子:
package com.xtfggef.string; /** * 字符串比較:equals()和==的區別 * @author 二青 * */ public class StringInit { public static void main(String[] args) { String s = "hello world"; String s1 = new String("hello world"); String s2 = new String("hello world"); String s3 = new String("hello"); String s4 = "hello world"; System.out.println(s.equals(s1));; System.out.println(s1.equals(s2)); System.out.println(s1.equals(s3)); System.out.println("------------------"); System.out.println(s == s1); System.out.println(s == s3); System.out.println(s == s4); } }
輸出:
true
true
false
------------------
false
false
true
此處驗證了一個問題,就是比較方法equals()和==的區別,一句話:equals()比較的是對象的內容,也就是JVM堆內存中的內容,==比較的是地址,也就是棧內存中的內容。
如上述代碼中,s、s一、s二、s4他們四個String對象的內容都是"hello world",因此,用equals()比較他們,返回的都是true。可是,當s和s1用==比較時,卻返回false,由於兩者在堆中開闢的地址不同,因此,返回的確定是false。而爲何s和s4用==比較時,返回的是true呢,由於上文中提到過,直接量的字符串會產生緩存池,因此,當聲明s4的時候,編譯器檢測到緩存池中存在相同的字符串,因此就直接使用,只要將s4指向s所指向的字符串就好了,兩者指向同一字符串,因此地址固然相等!
注意:此處隱藏着一個比較細的編程習慣,尤爲是用==進行比較的時候,儘可能將常量放在==的左邊,由於咱們有的時候,會不當心將==寫成=,這樣的話,若是將常量放在左邊,編譯器會報錯,提醒你,可是,若是將變量放在左邊,常量放右邊,即便你寫成了=,編譯器默認爲變量賦值了,所以也不會報錯。
由於String類實現了public interface Comparable<T>,而Comparable接口裏有惟一的方法:public int compareTo(T o)。因此,String類還有另外一個字符串比較方法:compareTo()
-----------------public int compareTo(String anotherString)---------------
compareTo()可實現比較兩個字符串的大小,源碼以下:
public int compareTo(String anotherString) { int len1 = count; int len2 = anotherString.count; int n = Math.min(len1, len2); char v1[] = value; char v2[] = anotherString.value; int i = offset; int j = anotherString.offset; if (i == j) { int k = i; int lim = n + i; while (k < lim) { char c1 = v1[k]; char c2 = v2[k]; if (c1 != c2) { return c1 - c2; } k++; } } else { while (n-- != 0) { char c1 = v1[i++]; char c2 = v2[j++]; if (c1 != c2) { return c1 - c2; } } } return len1 - len2; }
compareTo是怎麼實現的呢?
首先,會對兩個字符串左對齊,而後從左到右一次比較,若是相同,繼續,若是不一樣,則計算不一樣的兩個字符的ASCII值的差,返回就好了。與後面的其餘字符不要緊。
舉個例子:
package com.xtfggef.string; /** * compareTo()測試 * @author 二青 * */ public class CompareToTest { public static void main(String[] args) { String s = "hallo"; String s2 = "ha"; String s3 = "haeeo"; int a = s.compareTo(s2); System.out.println("a:"+a); int b = s.compareTo(s3); System.out.println("b:"+b); int c = s2.compareTo(s3); System.out.println("c:"+c); } }
程序輸出:
a:3
b:7
c:-3
s和s2相比,前兩個相同,若是是這種狀況,則直接返回length1-length2
s和s3相比,前兩個相同,不用管,直接用第三個字符的ASCII碼作差就好了。因此'l'-'a'=7
此處網友「handsomeman_wei」問我源碼中的c1-c2理解不了,就是上面紅字部分的解釋。
s2和s3相比,同第一種狀況同樣,只是length1比length2小,所以值爲負數。
-----------public char charAt(int index)-----------
獲取指定位置的字符,比較容易理解,源碼爲:
public char charAt(int index) { if ((index < 0) || (index >= count)) { throw new StringIndexOutOfBoundsException(index); } return value[index + offset]; }
String s = "hallo";
char a = s.charAt(2);
System.out.println(a);
輸出:l
注意:參數index的值從0到字符串的長度-1,因此,若是值不在這個範圍內,以下:
String s = "hallo";
char a = s.charAt(8);
System.out.println(a);
則報錯:
Exception in thread "main" java.lang.StringIndexOutOfBoundsException: String index out of range: 8
at java.lang.String.charAt(String.java:686)
at com.xtfggef.string.CompareToTest.main(CompareToTest.java:20)
與charAt()相對應的是indexOf():根據給定的字符串,返回他的位置。
indexOf()有多個參數:
public int indexOf(int ch)
public int indexOf(int ch, int fromIndex)
public int indexOf(String str)
public int indexOf(String str, int fromIndex)
static int indexOf(char[] source, int sourceOffset, int sourceCount,
char[] target, int targetOffset, int targetCount,
int fromIndex)
有興趣的本身去試試,這兒就很少闡述了。
-----------substring()------------
public String substring(int beginIndex) { return substring(beginIndex, count); }
用於截取字符串,此處與另外一個方法對比:
public String substring(int beginIndex, int endIndex) { if (beginIndex < 0) { throw new StringIndexOutOfBoundsException(beginIndex); } if (endIndex > count) { throw new StringIndexOutOfBoundsException(endIndex); } if (beginIndex > endIndex) { throw new StringIndexOutOfBoundsException(endIndex - beginIndex); } return ((beginIndex == 0) && (endIndex == count)) ? this : new String(offset + beginIndex, endIndex - beginIndex, value); }
前者調用後者來實現,前者截取從指定位置到字符串結束的子字符串,後者截取從指定位置開始,到endIndex-1位置的子字符串。
public class CompareToTest { public static void main(String[] args) { String s = "helloworld"; String s1 = s.substring(2); String s2 = s.substring(2, 7); String s3 = (String) s.subSequence(2, 7); System.out.print("s1:"+s1+"\n"+"s2:"+s2+"\n"+"s3:"+s3); } }
輸出:
s1:lloworld
s2:llowo
s3:llowo
細心的讀者應該看出來,該類裏面包含一個subSequence(),並且該方法與substring(int,int)返回的結果同樣,觀察下源碼,不難發現的區別:
public CharSequence subSequence(int beginIndex, int endIndex) { return this.substring(beginIndex, endIndex); } }
其實subSequence()內部就是調用的substring(beginIndex, endIndex),只是返回值不一樣。
subString返回的是String,subSequence返回的是實現了CharSequence接口的類,也就是說使用subSequence獲得的結果,只能使用CharSequence接口中的方法。不過在String類中已經重寫了subSequence,調用subSequence方法,能夠直接轉爲String對象,如咱們例子中的作法。
-----------------public String replace(char oldChar, char newChar)和public String replaceAll(String regex, String replacement)-------------------
public String replace(char oldChar, char newChar) { if (oldChar != newChar) { int len = count; int i = -1; char[] val = value; /* avoid getfield opcode */ int off = offset; /* avoid getfield opcode */ while (++i < len) { if (val[off + i] == oldChar) { break; } } if (i < len) { char buf[] = new char[len]; for (int j = 0 ; j < i ; j++) { buf[j] = val[off+j]; } while (i < len) { char c = val[off + i]; buf[i] = (c == oldChar) ? newChar : c; i++; } return new String(0, len, buf); } } return this; }
public String replaceAll(String regex, String replacement) { return Pattern.compile(regex).matcher(this).replaceAll(replacement); }
前者參數爲兩個字符串,用newChar替換原串裏的全部oldChar。
後者從第一個參數能夠看出,須要替換的東西能夠用正則表達式描述。例子以下:
package com.xtfggef.string; public class ReplaceTest { public static void main(String[] args) { String s = "hello world"; String s1 = s.replace("l", "d"); System.out.println(s1); String s2 = "a78e5opx587"; String s3 = s2.replaceAll("[0-9]", "");//用空串替換原串裏全部的0-9的數字 System.out.println(s3); } }
輸出:
heddo wordd
aeopx
-------------public String[] split(String regex)-----------
該方法用於分割字符串,獲得一個String類型的數組,根據regex可知,參數是個正則表達式。請看下面的例子:
package com.xtfggef.string; public class SpiltTest { public static void main(String[] args) { String s = "hello world"; String s1 = "hello.worldd"; String[] s2 = s.split(" "); String[] s3 = s1.split("\\."); for(int i=0; i<s2.length; i++){ System.out.print(s2[i]+" "); } System.out.println(); for(int j=0; j<s3.length; j++){ System.out.print(s3[j]+" "); } } }
輸出:
hello world
hello worldd
關於spilt()的其餘重載方法,可參見JDK的String類的實現。
spilt()須要注意的事項,就是當分隔符爲 . 的話,處理起來不同,必須寫成\\.由於.是正則表達式裏的一個特殊符號,必須進行轉義
--------------------public native String intern();--------------------(補充知識點:經網友java2000_wl提醒,特此補充,歡迎廣大讀者及時提出建議,我必將虛心接受!)
intern()方法和前面說的equals()方法關係密切,從public native String intern()看出,它是Java的本地方法,咱們先來看看Java文檔裏的描述:
Returns a canonical representation for the string object. A pool of strings, initially empty, is maintained privately by the class String.When the intern method is invoked, if the pool already contains a string equal to this String object as determined by theequals(Object) method, then the string from the pool is returned. Otherwise, this String object is added to the pool and a reference to this String object is returned. It follows that for any two strings s and t, s.intern()==t.intern() is true if and only if s.equals(t) is true. All literal strings and string-valued constant expressions are interned. @return a string that has the same contents as this string, but is guaranteed to be from a pool of unique strings.
意思就是說,返回字符串一個規範的表示。進一步解釋:有兩個字符串s和t,s.equals(t),則s.intern()==t.intern().舉個例子:
public class StringTest { public static void main(String[] args) { String s = new String("abc"); String s1 = "abc"; String s2 = "abc"; String s3 = s.intern(); System.out.println(s == s1);//false System.out.println(s == s2);//false System.out.println(s == s3);//false System.out.println(s1 == s3);//true } }
輸出結果如註釋所示,前兩個結果前面已經說的很清楚了,如今拿最後一個說明,首先看看s3 = s.intern()這句,當調用s.intern()這句的時候,先去字符串常量池中找,是否有abc這個串,若是沒有,則新增,同時返回引用,若是有,則返回已經存在的引用,此處s1和s2都指向常量池中的abc對象,因此此處是存在的,調用s.intern()後,s3和s一、s2指向同一個對象,因此s1==s3返回的是true。
intern()作到了一個很不尋常的行爲:在運行期動態的在方法區建立對象,通常只有像new關鍵字能夠在運行期在堆上面建立對象,因此此處比較特殊。屬於及時編譯的概念。
通常常見的字符串處理函數就這些,其它的還有不少,就不一一列舉。
三、一些常見的問題,處理結果
在咱們平常的開發中,總會遇到一些問題,在此我總結一下:
String s = "123" + "456"內存中產生幾個字符串對象?
這是個比較有爭議的問題,面試的時候,老師還挺喜歡問,論壇上你們說幾個的也有,我給你們分析一下,由於咱們前面有提到Java字符串的緩存機制,編譯器在編譯的時候會進行優化,因此在編譯的過程當中123和456被合成了一個字符串"123456",所以,若是緩存池中目前沒有123456這個對象,那麼會產生一個,即""123456",且棧中產生一個引用s指向它,若是緩存池中已經存在"123456",那麼將產生0個對象,直接用s指向它。
若是spilt()函數的參數在要分割的字符串中沒有怎麼辦?如String s = "helloworld" ,我如今調用String[] s2 = s.spilt("abc"),返回什麼?
這個問題是我曾經參加紅帽軟件面試的時候遇到的相關題,當時懵了,像這樣的題目,若是不親自遇到過,或者看過源代碼,很難準確的寫出來。
作一個簡單的測試,就能夠看得出來:
package com.xtfggef.string; public class StringSpilt { public static void main(String[] args) { String s = "helloworld"; String[] s2 = s.split("abc"); for (int i = 0; i < s2.length; i++) { System.out.println(s2[i] + " " + i); } } }
輸出:helloworld 0
說明當遇到源字符串中沒有的字符時,會把它整個串放入到數組中。spilt()的內部實現仍是挺複雜的,多層嵌套,不便於放到這兒分析。
關於字符串自動類型轉換分析
首先看一下題的類型:
int i = 2; int j = 3; String s = "9"; System.out.println(i+j+s); System.out.println("-----------------------"); System.out.println(i+s+j);
以上運算各輸出什麼?不妨猜猜
59
-----------------------
293
首先i+j=5,而後5和9天然鏈接,這裏涉及到java的自動類型轉換,此處int型的直接轉成String類型的。第二個依次鏈接,都轉化爲String類型的了。
補充(細節):看下面的程序:
String s = "ab"; String s1 = "a"; String s2 = s1 + "b"; String s3 = "ab"; System.out.println(s == s2);//false System.out.println(s2 == s3);//false System.out.println(s2.hashCode() == s3.hashCode()); String s4 = "ad"; String s5 = "a" + "d"; String s6 = "ad"; System.out.println(s4 == s5);//true System.out.println(s4 == s6);//true
此處主要是想說明:s1+"b"和"a"+"b"的不一樣,再看一段代碼:
System.out.println(s1.hashCode()); System.out.println(s2.hashCode()); System.out.println(s3.hashCode()); System.out.println(s4.hashCode()); System.out.println(s5.hashCode());
輸出:
97
3105
3105
3107
3107
說明s1+"b"的過程建立了新的對象,因此地址不同了。因此用==比較的話,返回的是false。
此處繼續補充:爲何s1+"b"會產生新的對象?而沒有去常量池查找是否已經存在ab對象,以至於s==s2返回false。由於咱們說過常量池(下文會講常量池)是在編譯期肯定好的,因此若是咱們的語句時String s5 = "ab"的話,這個是在編譯期肯定的,會去常量池查找,而此處咱們的語句時s2 = s1+"b",s2的值只有在運行期才能肯定,因此不會去常量池查找,也就是產生新串。再次提問:那麼這裏s2的值是在哪兒分配的呢?堆、JVM棧仍是運行時常量池?正確回答:s2在堆上分配,由於+的內部實現是用StringBuilder來實現的。String s2 = s1+"b" 內部是這樣實現的:String s2 = new StringBuilder(s1).append("b").toString();因此是在堆上來分配的
此處網友cowmich補充:調用s2.hashCode() == s3.hashCode()返回true。我解釋下:
==比較的是他們的地址,s1+"b"會產生一個新的串,因此和s和s2用==比,返回false,若是用equals的話,返回確定是true,由於equals()比較的是對象的內容(String類是這樣的)。至於hashCode,是這樣的:若是沒有重寫Object的hashCode(),那麼若是對象調用equals()放回true,則這兩個對象調用hashCode()後返回的整數必定相等。此處繼續補充:對於Object類而言,原生的equals()方法,必須兩個對象的地址和內容都同樣才返回true,同時Object類原生的hashCode()是參照對象的地址和內容根據必定的算法生產的。因此原生的hashCode()只有調用equals()返回true才相等。而String類不一樣,String類重寫了Object的equals(),放鬆了條件,只要對象地址或者內容相等就返回true,咱們看看源碼:
public boolean equals(Object anObject) { if (this == anObject) { return true; } if (anObject instanceof String) { String anotherString = (String)anObject; int n = count; if (n == anotherString.count) { char v1[] = value; char v2[] = anotherString.value; int i = offset; int j = anotherString.offset; while (n-- != 0) { if (v1[i++] != v2[j++]) return false; } return true; } } return false; }
同時,String類重寫了hashCode()方法,只要內容相等,則調用hashCode返回的整數值也相等,因此此處:s3和s2雖然地址不等,可是內容相等,因此會有:s2.hashCode() == s3.hashCode()返回true。可是這句話反過來說就不必定成立了,由於畢竟hashCode()只是一種算法。繼續補充:剛剛說了Object類和String類,此處補充下Integer類:Integer類,返回的哈希碼就是Integer對象裏所包含的那個整數的數值,例如Integer a=new Integer(50),則a.hashCode的值就是50 。因而可知,2個同樣大小的Integer對象,返回的哈希碼也同樣。
補充:應網友 KingBoxing 的要求,我作下關於常量池、字符串常量池、運行時常量池的介紹:
常量池通常就是指字符串常量池,是用來作字符串緩存的一種機制,當咱們在程序中寫了形如String s = "abc"這樣的語句後,JVM會在棧上爲咱們分配空間,存放變量s和對象」abc「,當咱們再次須要abc對象時,若是咱們寫下:String s1 = "abc"的語句時,JVM會先去常量池中找,若是不存在,則新建立一個對象。若是存在,則直接將s1指向以前的對象」abc「,此時,若是咱們用==來判斷的話,返回的true。這樣作的好處就是節省內存,系統響應的速度加快,(由於省去了對象的建立時間)這也是緩存系統存在的緣由。常量池是針對在編譯期間就肯定下來的常量而言的,如上所說的String類的一些對象。可是,當類被加載後,常量池會被搬到方法區的運行時常量池,此時就再也不是靜態的了,那麼是否是就不能向常量池中添加新的內容了呢(由於咱們剛剛說過,常量池是在編譯期肯定好的)?答案是否認的,咱們依然能夠在運行時向常量池添加內容!這就是咱們說過的String類有個方法叫intern(),它能夠在運行時將新的常量放於常量池。由於我在上文中已經詳細介紹過intern(),此處再也不贅述!
我的的力量是有限的,歡迎你們積極補充,同時也歡迎讀者隨時批評指正!
有問題請聯繫:egg
郵箱:xtfggef@gmail.com 微博:http://weibo.com/xtfggef
You have to believe in yourself.That's the secretof success!
2、StringBuffer、StringBuilder
一、初始化
StringBuffer和StringBuilder就是所謂的可變字符串類,共四個構造方法:
StringBuffer()
public StringBuffer(int paramInt)
public StringBuffer(String paramString)
public StringBuffer(CharSequence paramCharSequence)
觀察其源碼發現,使用StringBuffer()時,默認開闢16個字符的長度的空間,使用public StringBuffer(int paramInt)時開闢指定大小的空間,使用public StringBuffer(String paramString)時,開闢paramString.length+16大小的空間。都是調用父類的構造器super()來開闢內存。這方面StringBuffer和StringBuilder都同樣,且都實現AbstractStringBuilder類。
二、主要方法
兩者幾乎沒什麼區別,基本都是在調用父類的各個方法,一個重要的區別就是StringBuffer是線程安全的,內部的大多數方法前面都有關鍵字synchronized,這樣就會有必定的性能消耗,StringBuilder是非線程安全的,因此效率要高些。
public static void main(String[] args) throws Exception { String string = "0"; int n = 10000; long begin = System.currentTimeMillis(); for (int i = 1; i < n; i++) { string += i; } long end = System.currentTimeMillis(); long between = end - begin; System.out.println("使用String類耗時:" + between+"ms"); int n1 = 10000; StringBuffer sb = new StringBuffer("0"); long begin1 = System.currentTimeMillis(); for (int j = 1; j < n1; j++) { sb.append(j); } long end1 = System.currentTimeMillis(); long between1 = end1 - begin1; System.out.println("使用StringBuffer類耗時:" + between1+"ms"); int n2 = 10000; StringBuilder sb2 = new StringBuilder("0"); long begin2 = System.currentTimeMillis(); for (int k = 1; k < n2; k++) { sb2.append(k); } long end2 = System.currentTimeMillis(); long between2 = end2 - begin2; System.out.println("使用StringBuilder類耗時:" + between2+"ms"); }
輸出:
使用String類耗時:982ms雖然這個數字每次執行都不同,並且每一個機子的狀況也不同,可是有幾點是肯定的,String類消耗的明顯比另外兩個多得多。還有一點就是,StringBuffer要比StringBuilder消耗的多,儘管相差不明顯。
接下來介紹一些經常使用的方法。
-----------------------public synchronized int length()--------------------------
-------------------------public synchronized int capacity()---------------------------
兩者都是獲取字符串的長度,length()獲取的是當前字符串的長度,capacity()獲取的是當前緩衝區的大小。舉個簡單的例子:
StringBuffer sb = new StringBuffer(); System.out.println(sb.length());; System.out.println(sb.capacity());輸出:
0
16
StringBuffer sb = new StringBuffer("hello"); System.out.println(sb.length());; System.out.println(sb.capacity());輸出:
5
21
由於默認分配16個字符大小的空間,因此不難解釋上面的結果。
------------------public boolean equals(Object paramObject)---------------------
StringBuffer sb = new StringBuffer("hello"); StringBuffer sb2 = new StringBuffer("hello"); System.out.println(sb.equals(sb2));以上程序輸出false,是否是有點驚訝?記得以前咱們的文章說過,equals()比較的是字符串的內容,按理說此處應該輸出的是true纔對。
究其緣由,String類重寫了Object的equals(),因此只須要看內容是否相等便可,可是StringBuffer沒有重寫equals(),此處的equals()仍然是調用的Object類的,因此,調用StringBuffer類的equals(),只有地址和內容都相等的字符串,結果纔會返回true。
另外StringBuffer有一系列追加、插入、刪除字符串的方法,首先append(),就是在原來的字符串後面直接追加一個新的串,和String類相比有明顯的好處:
String類在追加的時候,源字符串不變(這就是爲何說String是不可變的字符串類型),和新串鏈接後,從新開闢一個內存。這樣就會形成每次鏈接一個新串後,都會讓以前的串報廢,所以也形成了不可避免的內存泄露。
//append() StringBuffer sb = new StringBuffer("helloworld, "); sb.append("I'm ").append("erqing ").append("who ").append("are you ?"); System.out.println(sb); //public synchronized StringBuffer insert(int paramInt, Object paramObject) sb.insert(12, /*9*/"nice! "); System.out.println(sb); //public synchronized StringBuffer reverse() sb.reverse(); System.out.println(sb); sb.reverse(); System.out.println(sb); //public synchronized StringBuffer delete(int paramInt1, int paramInt2) //public synchronized StringBuffer deleteCharAt(int paramInt) sb.delete(12, 18); System.out.println(sb); sb.deleteCharAt(5); System.out.println(sb);輸出:
helloworld, I'm erqing who are you ?
helloworld, nice! I'm erqing who are you ?
? uoy era ohw gniqre m'I !ecin ,dlrowolleh
helloworld, nice! I'm erqing who are you ?
helloworld, I'm erqing who are you ?
helloorld, I'm erqing who are you ?
-----------------public synchronized void trimToSize()---------------------
該方法用於將多餘的緩衝區空間釋放出來。
StringBuffer sb = new StringBuffer("hello erqing"); System.out.println("length:"+sb.length()); System.out.println("capacity:"+sb.capacity()); sb.trimToSize(); System.out.println("trimTosize:"+sb.capacity());輸出:
length:12
capacity:28
trimTosize:12
StringBuffer類還有不少方法,關於字符查找,截取,替換方面的方法,有興趣的童鞋能夠去研究研究源碼,定會學到很多知識!
3、字符串處理類StringTokenizer
StringTokenizer是java.util包下的一個類,用來對字符串作簡單的處理。
舉個簡單的例子:
String s = "Tonight is the answer !"; StringTokenizer st = new StringTokenizer(s," "); int count = st.countTokens(); System.out.println("個數爲:"+count); while (st.hasMoreTokens()) { String token = st.nextToken(); System.out.println(token); }輸出:
個數爲:5 Tonight is the answer !