JAVA雖然是在C++基礎上發展而來,但卻對C++的許多缺陷有所改進,其中一個不得不提的就是字符串,我們知道,隨著學習的深入,進入MFC時,當處理字符串或字符時,常會需要通過_T()宏將字符或字符串變成UNICODE型,否則,會在處理中出現BUG,而在JAVA中,字符char或存儲在Character類中的字符,不是一個字節,而是2個字節,采用UNICODE,這是為了支持全世界上的所有字符。
字符的序列組成字符串,有兩種類型的字符串:一種是創建以后不需要修改的,稱為字符串常量,在JAVA中,用String類存儲;
一種是創建以后需要對其進行修改的,稱為字符串變量,在JAVA中,用StringBuffer類操作和管理。
StringBuffer類
1、創建StringBuffer類對象
StringBuffer類對象表示的是字符串變量(注意是"變量"),每一個StringBuffer類對象都是可以擴充和修改的字符串變量。以下是常用的StringBuffer類構造函數:
(1)public StringBuffer()
創建一個新的空的StringBuffer類的對象,其容量初值設置成16個字符(注意是16個字符)
(2)public StringBuffer(int length)
創建一個新的空的StringBuffer類的對象,其容量初值設置成length個字符
(3)public StringBuffer(String str)
創建一個新的StringBuffer類的對象,其內容為str的內容,容量設置成str長度再加16個字符 (注意:再加上16個字符)
2、StringBuffer類對象的常用方法
(1)StringBuffer類對象的擴充
StringBuffer類提供兩組方法用來擴充StringBuffer對象所包含的字符,分別是:
1)public StringBuffer append
(Object obj)
append方法用于擴充StringBuffer對象所包含的字符,該方法將指定的參數對象轉化為字符串后,將其附加在原來的StringBuffer對象之后,并返回新的StringBuffer對象。附加的的參數對象可以是各種數據類型的,如int、char、String、double等。
2)public StringBuffer insert(
int插入位置offset,參數對象類型,參數對象名)
該方法將指定的參數對象轉化為字符串后,將其插入在原來的StringBuffer對象中指定的位置,并返回新的StringBuffer對象。
(2)StringBuffer類對象的長度與容量
一個StringBuffer類對象的長度指的是它包含的字符個數;容量指的是被分配的字符空間的數量。
1)public int length()
該方法返回當前StringBuffer類對象包含的字符個數。
2)public int capacity()
該方法返回當前StringBuffer類對象分配的字符空間的數量。
(3)StringBuffer類對象的修改
public void setCharAt(intindex,charch)
該方法將當前StringBuffer對象中的index位置的字符替換為指定的字符ch。
(4)字符串的賦值和加法
字符串是在程序中要經常使用的數據類型,在Java編譯系統中引入了字符串的賦值和加法操作。
(5)其它方法類似String類的方法
3、利用StringTokenizer類分解字符串
StringTokenizer類位于java.util包中, 在使用該類時在程序開始加上
importjava.util.StringTokenizer或
importjava.util.*
StringTokenizer類
對于StringTokenizer類,其主要作用是將字符串按照給定的分割符進行分割,其功能和String類的split方法類似
1、StringTokenizer類的構造函數
(1)StringTokenizer(Stringstr)
為給定的字符串str創建一個StringTokenizer對象,其分隔符默認設置為“\t\n\r\f”,亦即:空格、水平制表符tab、換行、回車、表格符
(2)StringTokenizer(String str,String delim)
為給定的字符串str創建一個StringTokenizer對象,其分隔符為指定的字符串delim,默認不包含分隔符
3)StringTokenizer(String str,String delim,boolean returnDelims)
為給定的字符串str創建一個StringTokenizer對象,其分隔符為指定的字符串delim,如果returnDelims為true,則創建的StringTokenizer對象中的每個字符串包含有分隔符,否則不包含分隔符
2、StringTokenizer類的常用方法
nIntcountTokens()
返回StringTokenizer對象中被分割后子字符串的個數
nBooleanhasMoreElements()
該方法的功能和hasMoreTokens()方法的功能一樣
nBooleanhasMoreTokens()
檢測StringTokenizer對象中是否包含分割好的子字符串,有則返回true,否則返回false
ObjectnextElement()
該方法具有nextToken()一樣的功能,主要區別是它返回的不是String對象,而是一個Object對象
StringnextToken()
返回StringTokenizer對象中下一個分割好的子字符串
StringnextToken(String delim)
返回StringTokenizer對象中下一個分割好的子字符串,但是分隔符被重新設定為delim
n其實在有些編程語言中,比如C語言,其字符串就是由字符數組構成的,每個字符串的結尾用“\0”標志,但是在Java中卻不是這樣的。
n在Java中,字符串通常是作為String類的對象存在著,如:Strings=“I like Java!”,其中“Ilike Java!”就是一個對象。
所以說,Java中的字符串和字符數組是完全不相同的,和C語言中的字符串也是不一樣的!
n為了方便字符串和字符數組的轉換,在String類中提供了許多這樣的構造函數和方法
n如構造函數String(char[] value)
n方法toCharArray()
方法valueOf(char[] data)
常量池
對于源程序中出現的字符串常量,當程序運行時,會統一保存到一個常量池中進行緩存。
對引用這些緩存在常量池中的字符串的變量進行比較,用==也會得到正確的結果。
但在運行時,對字符串的各種操作如+、substring等等,都是會產生新的字符串對象的。
但是強大的編譯器會對字符串常量的拼接進行優化,諸如s3 = "hell" + "o"時,s3仍然會
指向常量池中的字符串。但對于變量的運算,總不能要求虛擬機執行諸如s1 + s2時還要
判斷結果是否已在常量池中了吧。因此,要用equals而非==去判斷兩個字符串是否相等。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
public static void main(String[] args) { // String constants are put in constant pool. String s1 = "hello" ; String s2 = "hello" ; String s3 = "hell" + "o" ; System.out.println(s1 == s2); System.out.println(s1 == s3); // Operation like +,substring on string create new one. String s4 = "hell" ; String s5 = s4 + "o" ; System.out.println(s1 == s5); System.out.println(s1.equals(s5)); // substring has special handle on substring(0) String s6 = s1.substring( 0 ); System.out.println(s1 == s6); } |
測試代碼s1、s2、s3的字節碼:
0: ldc #16; //String hello
2: astore_1
3: ldc #16; //String hello
5: astore_2
6: ldc #16; //String hello
8: astore_3
測試代碼s4、s5的字節碼:
41: ldc #30; //String hell
43: astore 4
45: new #32; //class java/lang/StringBuilder
48: dup
49: aload 4
51: invokestatic #34; //Method java/lang/String.valueOf:(Ljava/lang/Object;)Ljava/lang/String;
54: invokespecial #40; //Method java/lang/StringBuilder."<init>":(Ljava/lang/String;)V
57: ldc #43; //String o
59: invokevirtual #45; //Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
62: invokevirtual #49; //Method java/lang/StringBuilder.toString:()Ljava/lang/String;
注意一點是substring方法,substring(0,3)是得到從字符0到2的字符串。這樣設計的原因
也許是這樣容易計算子串的長度,3-0=3。同時substring對于特殊參數有特別的優化處理:
1
2
3
4
5
6
7
8
9
10
11
12
13
|
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); } |
由此看出,String對象背后并沒有什么神奇之處,對字節碼有了些了解可以更好的理解它。
其實常量池中還保存類及其方法的很多信息,如包名、類名、方法簽名等等,有興趣可以
深入研究。