在第7章已对Java的字符串处理做了简要的介绍。本章将对此做详细论述。像大多数其他计算机语言一样,Java中的字符串也是一连串的字符。但是与许多其他的计算机语言将字符串作为字符数组处理不同,Java将字符串作为String类型对象来处理。将字符串作为内置的对象处理允许Java提供十分丰富的功能特性以方便处理字符串。例如,Java语言中有多种方法用于比较两个字符串,搜索子字符串,连接字符串以及改变字符串中字母的大小写。也有许多途径可以构造出String对象,使得当需要时,能够容易得到字符串。有些出乎意料的是当创建一个String对象时,被创建的字符串是不能被改变的。这也就是说一旦一个String对象被创建,将无法改变那些组成字符串的字符。表面上看起来,这好像是一个严格的约束。然而事实并非如此。你仍能够执行各种类型的字符串操作。区别在于每次需要改变字符串时都要创建一个新的String对象来保存新的内容。原始的字符串不变。之所以采用这种方法是因为实现固定的,不可变的字符串比实现可变的字符串更高效。对于那些想得到改变的字符串的情况,有一个叫做StringBuffer的String类的友类。它的对象包含了在创建之后可被改变的字符串。String类和StringBuffer类都在java.lang中定义。因此它们可以自动的被所有程序利用。两者均被说明为final,这意味着两者均不含子类。从而使得某些增强性能的优化可作用于普通字符串操作。最后需要指明一点:包含在类型String对象中的字符串的不可改变意味着String实例一旦被建立,它的内容将不能被改变。然而在任何时候,被说明为String引用的变量可以被改变以指向另外的一些字符串(String)对象。
13.1 String构造函数
String类支持几种构造函数。将创建空String的构造函数称为默认构造函数。例如,
String s = new String();
将创建一个String实例,该实例中不包含字符。通常希望创建含有初始值的字符串。String类提供了各种构造函数来完成这项功能。使用如下的构造函数可以创建一个被字符数组初始化的字符串(String):
String(char chars[ ])
下面是一个例子:
char chars[] = { 'a', 'b', 'c' };
String s = new String(chars);
这个构造函数用字符串“abc”初始化s。
使用下面的构造函数可以指定字符数组的一个子区域作为初始化值。
String(char chars[ ], int startIndex, int numChars)
这里,startIndex指定了子区域开始的下标,numChars指定所用字符的个数。下面是一个例子:
char chars[] = { 'a', 'b', 'c', 'd', 'e', 'f' };
String s = new String(chars, 2, 3);
该例子用字符cde初始化s。用下面的构造函数可以构造一个String对象,该对象包括了与另一个String对象相同的字符序列。
String(String strObj)
这里strObj是一个String对象,请看如下例子:
// Construct one String from another.
class MakeString {
public static void main(String args[]) {
char c[] = {'J', 'a', 'v', 'a'};
String s1 = new String(c);
String s2 = new String(s1);
System.out.println(s1);
System.out.println(s2);
}
}
程序的输出如下所示:
Java
Java
正如你所能看到的,s1和s2包含了相同的字符串。尽管Java的Char类型使用16位(bit)表示Unicode编码字符集,在Internet中,字符串的典型格式使用由ASCII字符集构成的8位字节数组。因为8位ASCII字符串是共同的,当给定一个字节(byte)数组时,String类提供了初始化字符串的构造函数。它们的形式如下:
String(byte asciiChars[ ])
String(byte asciiChars[ ], int startIndex, int numChars)
这里asciiChars指定了字节数组。第二种形式允许指定一个子区域。在这些构造函数中,通过使用操作平台默认的字符编码实现了由字节到字符的转换,下面的程序举例说明了这些构造函数:
// Construct string from subset of char array.
class SubStringCons {
public static void main(String args[]) {
byte ascii[] = {65, 66, 67, 68, 69, 70 };
String s1 = new String(ascii);
System.out.println(s1);
String s2 = new String(ascii, 2, 3);
System.out.println(s2);
}
}
该程序运行产生如下的输出:
ABCDEF
CDE
字节-字符串转换的扩展版本也有定义,使用该版本,你可以指定实现字节-字符串转换的字符编码方式。不过,大多数情况下,一般会选择操作平台提供的默认编码。注意:当从一个数组创建一个String对象时,数组的内容将被复制。在字符串被创建以后,如果改变数组的内容,String将不会随之改变。
13.2 字符串长度
字符串的长度是指其所包含的字符的个数。调用如下的length( )方法可以得到这个值:
int length( )
下面的程序段输出“3”,因为在字符串s中有三个字符。
char chars[] = { 'a', 'b', 'c' };
String s = new String(chars);
System.out.println(s.length());
13.3 特殊的字符串操作
因为字符串是程序中的一个通用和重要的部分,Java为字符串操作在语法中增加了特殊的支持。这些操作包括从字符串常量自动创建新的String实例。通过+运算符连接多个String对象以及将其他的数据类型转换成字符串形式。尽管有显式的方法去执行这些函数,而为了方便程序员,Java会自动执行它们。
13.3.1 字符串文字
前面的例子说明了如何通过使用new运算符从一个字符数组明确地创建一个String实例。然而这是一种早期的使用字符串常量的处理方法。对于程序中的每一个字符串常量,Java会自动创建String对象。因此,可以使用字符串常量初始化String对象。例如,如下的程序代码段创建两个相等的字符串。
char chars[] = { 'a', 'b', 'c' };
String s1 = new String(chars);
String s2 = "abc"; // use string literal
由于对应每一个字符串常量,有一String对象被创建,因此在使用字符串文字的任何地方,都可以使用String对象。例如,可以直接对引用字符串调用方法,如同它是一个对象引用,如下面语句所显示的那样。以字符串“abc”调用方法length( ),正如所料,输出“3”。
System.out.println("abc".length());
13.3.2 字符串连接
通常,Java不允许对String对象进行操作。这一规则的一个例外是+运算符,它可以连接两个字符串,产生一个String对象。也允许使用一连串的+运算符,例如下面的程序段将三个字符串连接起来:
String age = "9";
String s = "He is " + age + " years old.";
System.out.println(s);
输出为字符串“He is 9 years old.”。字符串连接的一个实际使用是当创建一个很长的字符串时,可以将它拆开,使用+将它们连接起来,避免源代码中长字符串的换行,下面是一个例子:
// Using concatenation to prevent long lines.
class ConCat {
public static void main(String args[]) {
String longStr = "This could have been " +
"a very long line that would have " +
"wrapped around. But string concatenation " +
"prevents this.";
System.out.println(longStr);
}
}
13.3.3 字符串与其他类型数据的连接
字符串可以和其他类型的数据连接。例如,考虑与前面的例子略有差别的一个例子:
int age = 9;
String s = "He is " + age + " years old.";
System.out.println(s);
在这里,age是一个整型(int)而不是另一个字符串(String)型值,但是程序的输出与前面的例子相同。这是因为在字符串(String)对象中age的整型(int)值自动转换为它的字符串(String)形式。然后这个字符串就和前面一样被连接。只要+运算符的一个运算数是字符串(String)实例,编译器就将另一个运算数转换为它的字符串形式。应当小心的是当你将其他类型的操作与字符串连接表达式混合时,有可能得到意想不到的结果。看下面例子:
String s = "four: " + 2 + 2;
System.out.println(s);
程序显示:
four: 22
而不是
four: 4
虽然这可能是你期望得到的。这是为什么呢?这是因为运算符的优先级造成了首先是“four”和与2相应的字符串的连接,这个连接的结果再和与2相应的字符串连接。若要先实现整数加,就应该使用圆括号。像下面这样:
String s = "four: " + (2 + 2);
现在s包含了字符串“four:4”。
13.3.4 字符串转换和toString( )
当Java在连接时将数据转换为其字符串形式时,它是通过调用一个由字符串(String)定义的字符串转换方法valueOf( )的重载来完成的。valueOf( )方法对所有简单的类型和类型Object重载。对于简单类型,valueOf( )方法返回一个字符串,该字符串包含了相应其被调用的值的可读值。对于对象,valueOf( )方法调用toString( )方法。在本章后面我们将更仔细地分析valueOf( )方法。这里让我们讨论toString( )方法,因为通过它可以确定所创建类的对象的字符串形式。每一个类都执行toString( )方法,因为它是由Object定义的。然而toString( )方法的默认实现是不够的。对于所创建的大多数类,通常想用你自己提供的字符串表达式重载toString( )方法。幸运的是这很容易做到。toString( )方法具有如下的一般形式:
String toString( )
实现toString( )方法,仅仅返回一个String对象,该对象包含描述类中对象的可读的字符串。通过对所创建类的toString( )方法的覆盖,允许将得到的字符串完全集成到Java的程序设计环境中。例如它们可以被用于print( )和println( )语句以及连接表达式中。下面的程序中用Box类重载toString( )方法说明了这些。
// Override toString() for Box class.
class Box {
244 第2 部分 Java 库
double width;
double height;
double depth;
Box(double w, double h, double d) {
width = w;
height = h;
depth = d;
}
public String toString() {
return "Dimensions are " + width + " by " +
depth + " by " + height + ".";
}
}
class toStringDemo {
public static void main(String args[]) {
Box b = new Box(10, 12, 14);
String s = "Box b: " + b; // concatenate Box object
System.out.println(b); // convert Box to string
System.out.println(s);
}
}
该程序的输出如下所示:
Dimensions are 10.0 by 14.0 by 12.0
Box b: Dimensions are 10.0 by 14.0 by 12.0
正如你能看到的那样,当Box对象在连接表达式中使用或出现在调用println( )中时,Box的toString( )方法被自动调用。
13.4 字 符 截 取
String类提供了许多从String对象中截取字符的方法。下面逐一介绍。尽管在一个String对象中构成字符串的字符不能像字符数组一样被索引,许多字符串(String)方法利用下标(或偏移)对字符串进行操作。和数组一样,字符串下标从0开始。
13.4.1 charAt( )
为了从一个字符串(String)中截取一个字符,可以通过charAt( )方法直接引用单个字符。其一般形式如下:
char charAt(int where)
这里,where是想要得到的字符的下标。where的值必须是非负的,它指定了在字符串中的位置。charAt( )方法返回指定位置的字符。例如,
char ch;
ch = "abc".charAt(1);
将“b”赋给ch。
13.4.2 getChars( )
如果想一次截取多个字符,可以使用getChars( )方法。它有如下的一般形式:
void getChars(int sourceStart, int sourceEnd, char target[ ], int targetStart)
这里sourceStart指定了子字符串开始的下标,sourceEnd指定了子字符串结束的下一个字符的下标。因此子字符串包含了从sourceStart到sourceEnd–1的字符。获得字符的数组由target所指定。将被复制子字符串于其中的target的下标由targetStart指定。注意必须确保的是数组target应该足够大以保证能容纳被指定子字符串中的字符。
下面的程序说明了getChars( )方法:
class getCharsDemo {
public static void main(String args[]) {
String s = "This is a demo of the getChars method.";
int start = 10;
int end = 14;
char buf[] = new char[end - start];
s.getChars(start, end, buf, 0);
System.out.println(buf);
}
}
该程序的输出如下所示:
demo
13.4.3 getBytes( )
有一种称为getBytes( )的方法,它是实现将字符存放于字节数组中的getChars( )方法的
替代,它使用平台提供的默认的字符到字节的转换。下面是它的最简单形式:
byte[ ] getBytes( )
也可使用getBytes( )方法的其他形式。在将字符串(String)值输出到一个不支持16位Unicode编码的环境时,getBytes( )是最有用的。例如,大多数Internet协议和文本文件格式在文本交换时使用8位ASCII编码。
13.4.4 toCharArray( )
如果想将字符串(String)对象中的字符转换为一个字符数组,最简单的方法就是调用
toCharArray( )方法。对应整个字符串,它返回一个字符数组。其一般形式为:
char[ ] toCharArray( )
这个函数是为了便于使用而提供的,因此也可以用getChars( )方法获得相同的结果。
13.5 字符串比较
String类包括了几个用于比较字符串或字符串内子字符串的方法。下面分别对它们进行介绍。13.5.1 equals( )和 equalsIgnoreCase( )
使用equals( )方法比较两个字符串是否相等。它具有如下的一般形式:
boolean equals(Object str)
这里str是一个用来与调用字符串(String)对象做比较的字符串(String)对象。如果两个字符串具有相同的字符和长度,它返回true,否则返回false。这种比较是区分大小写的。为了执行忽略大小写的比较,可以调用equalsIgnoreCase( )方法。当比较两个字符串时,它会认为A-Z和a-z是一样的。其一般形式如下:
boolean equalsIgnoreCase(String str)
这里,str是一个用来与调用字符串(String)对象做比较的字符串(String)对象。如果两个字符串具有相同的字符和长度,它也返回true,否则返回false。下面的例子说明了equals( )和equalsIgnoreCase( )方法:
// Demonstrate equals() and equalsIgnoreCase().
class equalsDemo {
public static void main(String args[]) {
String s1 = "Hello";
String s2 = "Hello";
String s3 = "Good-bye";
String s4 = "HELLO";
System.out.println(s1 + " equals " + s2 + " -> " +
s1.equals(s2));
System.out.println(s1 + " equals " + s3 + " -> " +
s1.equals(s3));
System.out.println(s1 + " equals " + s4 + " -> " +
s1.equals(s4));
System.out.println(s1 + " equalsIgnoreCase " + s4 + " -> " +
s1.equalsIgnoreCase(s4));
}
}
该程序的输出如下所示:
Hello equals Hello -> true
Hello equals Good-bye -> false
Hello equals HELLO -> false
Hello equalsIgnoreCase HELLO -> true
13.5.2 regionMatches( )
regionMatches( )方法将一个字符串中指定的区间和另一字符串中指定的区间进行比较。它的重载形式允许在比较时忽略大小写。下面给出这两种方法的一般形式:
boolean regionMatches(int startIndex, String str2,
int str2StartIndex, int numChars)
boolean regionMatches(boolean ignoreCase,
int startIndex, String str2,
int str2StartIndex, int numChars)
对于这两种形式,startIndex指定了调用字符串(String)对象内区间开始的下标。用于比较的字符串(String)由str2指定的。在str2内,开始比较区间的下标由str2StartIndex指定。用于比较的子字符串的长度在numChars中。在第二种方案中,如果ignoreCase是true,字符的大小写被忽略。否则,大小写是有意义的。
13.5.3 startsWith( )和endsWith( )
字符串(String)定义两个例程,它们或多或少是regionMatches( )方法的特殊形式。startsWith( )方法判断一个给定的字符串(String)是否从一个指定的字符串开始。相反地,endsWith( )方法判断所讨论的字符串(String)是否是以一个指定的字符串结尾。它们具有如下的一般形式:
boolean startsWith(String str)
boolean endsWith(String str)
这里,str是被测试的字符串(String)。如果字符串匹配,返回true。否则返回false。例如:
"Foobar".endsWith("bar")
和
"Foobar".startsWith("Foo")
都是true。
下面给出startsWith( )方法的第二种形式。这种形式允许指定一个起始点:
boolean startsWith(String str, int startIndex)
这里,startIndex指定了调用字符串开始搜索的下标。例如,
"Foobar".startsWith("bar", 3)
返回true。
13.5.4 equals( )与==的比较
理解equals( )方法和==运算符执行的是两个不同的操作是重要的。如同刚才解释的那样,equals( )方法比较字符串(String)对象中的字符。而==运算符比较两个对象引用看它们是否引用相同的实例。下面的程序说明了两个不同的字符串(String)对象是如何能够包含相同字符的,但同时这些对象引用是不相等的:
// equals() vs ==
class EqualsNotEqualTo {
public static void main(String args[]) {
String s1 = "Hello";
String s2 = new String(s1);
System.out.println(s1 + " equals " + s2 + " -> " +
s1.equals(s2));
System.out.println(s1 + " == " + s2 + " -> " + (s1 == s2));
}
}
变量s1指向由“Hello”创建的字符串(String)实例。s2所指的的对象是以s1作为初始化而创建的。因此这两个字符串(String)对象的内容是一样的。但它们是不同的对象,这就意味着s1和s2没有指向同一的对象,因此它们是不==的,上面例子的结果如下:
Hello equals Hello -> true
Hello == Hello -> false
13.5.5 compareTo( )
通常,仅仅知道两个字符串是否相同是不够的。对于排序应用来说,必须知道一个字符串是大于、等于还是小于另一个。一个字符串小于另一个指的是它在字典中先出现。而一个字符串大于另一个指的是它在字典中后出现。字符串(String)的compareTo( )方法实现了这种功能。它的一般形式如下:
int compareTo(String str)
这里str是与调用字符串(String)比较的字符串(String)。比较的结果返回并被解释如表13-1所示:
表13.1 字符串比较的结果及其含义
值 含义
小于0 调用字符串小于str
大于0 调用字符串大于str
等于0 两个字符串相等
下面是一个对字符串数组进行排序的例子程序。程序中在冒泡法排序中使用compareTo( )方法确定排序的顺序:
// A bubble sort for Strings.
class SortString {
static String arr[] = {
"Now", "is", "the", "time", "for", "all", "good", "men",
"to", "come", "to", "the", "aid", "of", "their", "country"
};
public static void main(String args[]) {
for(int j = 0; j < arr.length; j++) {
for(int i = j + 1; i < arr.length; i++) {
if(arr[i].compareTo(arr[j]) < 0) {
String t = arr[j];
arr[j] = arr[i];
arr[i] = t;
}
}
System.out.println(arr[j]);
}
}
}
程序的输出是如下的单词表:
Now
aid
all
come
country
for
good
is
men
of
the
the
their
time
to
to
正如从这个例子的输出所能看到的,compareTo( )方法区分单词的大小写。单词“Now”因为是以大写字母开始的而出现在其他单词的前面,这意味着它在ASCII字符集中具有更小的值。如果想在比较两个字符串时,忽略大小写,可以使用如下的compareToIgnoreCase( )方法:
int compareToIgnoreCase(String str)
除了忽略大小写之外,该方法的返回值与compareTo( )方法相同。该方法是在Java 2中新增加的。可以在前面的程序中换成这个方法。这样做了之后,“Now”将不再是第一个输出了。
13.6 搜索字符串
String类提供了两个方法,允许在字符串中搜索指定的字符或子字符串:
· indexOf( ) 搜索字符或子字符串首次出现。
· lastIndexOf( ) 搜索字符或子字符串的最后一次出现。
这两种方法被几种不同的方法重载。在所有这些情况下,方法返回字符或子字符串被发现的位置的下标,当搜索失败时,返回-1。
搜索字符首次出现用
int indexOf(int ch)
搜索字符最后一次出现用
int lastIndexOf(int ch)
这里ch是被查找的字符。
搜索子字符串首次或最后一次出现用
int indexOf(String str)
int lastIndexOf(String str)
这里子字符串由str指定
可以使用如下这些形式指定搜索的起始点:
int indexOf(int ch, int startIndex)
int lastIndexOf(int ch, int startIndex)
int indexOf(String str, int startIndex)
int lastIndexOf(String str, int startIndex)
这里startIndex指定了搜索开始点的下标。对于indexOf( )方法,搜索从startIndex开始到字符串结束。对于lastIndexOf( )方法,搜索从startIndex开始到下标0。下面的例子说明如何利用不同的索引方法在字符串(String)的内部进行搜索:
// Demonstrate indexOf() and lastIndexOf().
class indexOfDemo {
public static void main(String args[]) {
String s = "Now is the time for all good men " +
"to come to the aid of their country.";
System.out.println(s);
System.out.println("indexOf(t) = " +
s.indexOf('t'));
System.out.println("lastIndexOf(t) = " +
s.lastIndexOf('t'));
System.out.println("indexOf(the) = " +
s.indexOf("the"));
System.out.println("lastIndexOf(the) = " +
s.lastIndexOf("the"));
System.out.println("indexOf(t, 10) = " +
s.indexOf('t', 10));
System.out.println("lastIndexOf(t, 60) = " +
s.lastIndexOf('t', 60));
System.out.println("indexOf(the, 10) = " +
s.indexOf("the", 10));
System.out.println("lastIndexOf(the, 60) = " +
s.lastIndexOf("the", 60));
}
}
下面是该程序的输出结果:
Now is the time for all good men to come to the aid of their country.
indexOf(t) = 7
lastIndexOf(t) = 65
indexOf(the) = 7
lastIndexOf(the) = 55
indexOf(t, 10) = 11
lastIndexOf(t, 60) = 55
indexOf(the, 10) = 44
lastIndexOf(the, 60) = 55
13.7 修改字符串
因为字符串(String)对象是不可改变的,每当想修改一个字符串(String)时,就必须采用或者将它复制到StringBuffer或者使用下面字符串(String)方法中的一种,这些方法都将构造一个完成修改的字符串的拷贝。
13.7.1 substring( )
利用substring( )方法可以截取子字符串,它有两种形式。其中第一种形式如下:
String substring(int startIndex)
这里startIndex指定了子字符串开始的下标。这种形式返回一个从startIndex开始到调用字符串结束的子字符串的拷贝。substring( )方法的第二种形式允许指定子字符串的开始和结束下标:
String substring(int startIndex, int endIndex)
这里startIndex指定开始下标,endIndex指定结束下标。返回的字符串包括从开始下标直到结束下标的所有字符,但不包括结束下标对应的字符。下面的程序使用substring( )方法完成在一个字符串内用一个子字符串替换另一个子字符串的所有实例的功能:
// Substring replacement.
class StringReplace {
public static void main(String args[]) {
String org = "This is a test. This is, too.";
String search = "is";
String sub = "was";
String result = "";
int i;
do { // replace all matching substrings
System.out.println(org);
i = org.indexOf(search);
if(i != -1) {
result = org.substring(0, i);
result = result + sub;
result = result + org.substring(i + search.length());
org = result;
}
} while(i != -1);
}
}
这个程序的输出如下所示:
This is a test. This is, too.
Thwas is a test. This is, too.
Thwas was a test. This is, too.
Thwas was a test. Thwas is, too.
Thwas was a test. Thwas was, too.
13.7.2 concat( )
使用concat( )可以连接两个字符串,一般形式如下:
String concat(String str)
这个方法创建一个新的对象,该对象包含调用字符串。而str的内容跟在调用字符串的后面。concat( )方法与+运算符执行相同的功能。例如,
String s1 = "one";
String s2 = s1.concat("two");
将字符串“onetwo”赋给s2。它和下面的程序段产生相同的结果:
String s1 = "one";
String s2 = s1 + "two";
13.7.3 replace( )
replace( )方法用另一个字符代替调用字符串中一个字符的所有具体值。它具有如下的一般形式:
String replace(char original, char replacement)
这里original指定被由replacement指定的字符所代替的字符,返回得到的字符串。例如:
String s = "Hello".replace('l', 'w');
将字符串“Hewwo”赋给s。
13.7.4 trim( )
trim( )方法返回一个调用字符串的拷贝,该字符串是将位于调用字符串前面和后面的空白符删除后的剩余部分。它的一般形式如下:
String trim( )
这里是一个例子:
String s = " Hello World ".trim();
将字符串“Hello World”赋给s。
trim( )方法在处理用户输入的命令时,是十分有用的。例如,下面的程序提示用户输入一个州名后显示该州的首府名。程序中使用trim( )方法删除在用户输入期间,不经意间输入的任何前缀或后缀空白符。
// Using trim() to process commands.
import java.io.*;
class UseTrim {
public static void main(String args[])
throws IOException
{
// create a BufferedReader using System.in
BufferedReader br = new
BufferedReader(new InputStreamReader(System.in));
String str;
System.out.println("Enter 'stop' to quit.");
System.out.println("Enter State: ");
do {
str = br.readLine();
str = str.trim(); // remove whitespace
if(str.equals("Illinois"))
System.out.println("Capital is Springfield.");
else if(str.equals("Missouri"))
System.out.println("Capital is Jefferson City.");
else if(str.equals("California"))
System.out.println("Capital is Sacramento.");
else if(str.equals("Washington"))
System.out.println("Capital is Olympia.");
// ...
} while(!str.equals("stop"));
}
}
13.8 利用valueOf( )方法实现数据转换
valueOf( )方法将数据的内部格式转换为可读的形式。它是一种静态方法,对于所有Java内置的类型,在字符串(String)内被重载,以便每一种类型都能被转换成字符串。valueOf( )方法还被类型Object重载,所以创建的任何形式类的对象也可被用作一个参数(我们知道Object是所有的类的超类)。这里是它的几种形式:
static String valueOf(double num)
static String valueOf(long num)
static String valueOf(Object ob)
static String valueOf(char chars[ ])
与前面的讨论一样,调用valueOf( )方法可以得到其他类型数据的字符串形式——例如在进行连接操作时。对各种数据类型,可以直接调用这种方法得到合理的字符串(String)形式。所有的简单类型数据转换成相应于它们的普通字符串(String)形式。任何传递给valueOf( )方法的对象都将返回对象的toString( )方法调用的结果。事实上,也可以通过直接调用toString( )方法而得到相同的结果。
对大多数数组,valueOf( )方法返回一个相当晦涩的字符串,这说明它是一个某种类型的数组。然而对于字符(char)数组,它创建一个包含了字符(char)数组中的字符的字符串(String)对象。valueOf( )方法有一种特定形式允许指定字符(char)数组的一个子集。它具有如下的一般形式:
static String valueOf(char chars[ ], int startIndex, int numChars)
这里chars是存放字符的数组,startIndex是字符数组中期望得到的子字符串的首字符下标,numChars指定子字符串的长度。
13.9 改变字符串内字符的大小写
toLowerCase( ) 方法将字符串内的所有字符从大写字母改写为小写字母。而toUpperCase( )方法将字符串内所有字符从小写字母改写为大写字母。对于那些非字母字符,如数字等则不受影响。下面是这些方法的一般形式:
String toLowerCase( )
String toUpperCase( )
两种方法返回与调用字符串(String)对应的大写或小写的字符串(String)对象。下面是一个使用toLowerCase( )和toUpperCase( )方法的例子:
// Demonstrate toUpperCase() and toLowerCase().
class ChangeCase {
public static void main(String args[])
{
String s = "This is a test.";
System.out.println("Original: " + s);
String upper = s.toUpperCase();
String lower = s.toLowerCase();
System.out.println("Uppercase: " + upper);
System.out.println("Lowercase: " + lower);
}
}
程序的输出显示如下:
Original: This is a test.
Uppercase: THIS IS A TEST.
Lowercase: this is a test.
13.10 StringBuffer
StringBuffer是提供了大量的字符串功能的字符串(String)类的对等类。正如你所知,字符串(String)表示了定长,不可变的字符序列。相反,StringBuffer表示了可变长的和可写的字符序列。StringBuffer可有插入其中或追加其后的字符或子字符串。StringBuffer可以针对这些添加自动地增加空间,同时它通常还有比实际需要更多的预留字符,从而允许增加空间。Java大量使用这两种类,但是多数程序员仅仅处理字符串(String)而通过重载+运算符让Java在后台处理StringBuffer。
13.10.1 StringBuffer构造函数
StringBuffer定义了下面三个构造函数:
StringBuffer( )
StringBuffer(int size)
StringBuffer(String str)
默认构造函数(无参数)预留了16个字符的空间。该空间不需再分配。第二种形式接收一个整数参数,清楚地设置缓冲区的大小。第三种形式接收一个字符串(String)参数,设置StringBuffer对象的初始内容,同时不进行再分配地多预留了16个字符的空间。当没有指定缓冲区的大小时,StringBuffer分配了16个附加字符的空间,这是因为再分配在时间上代价很大。而且频繁地再分配可以产生内存碎片。StringBuffer通过给一些额外的字符分配空间,减少了再分配操作发生的次数。
13.10.2 length( )和capacity( )
通过调用length( )方法可以得到当前StringBuffer的长度。而通过调用capacity( )方法可以得到总的分配容量。它们的一般形式如下:
int length( )
int capacity( )
这里是一个例子:
// StringBuffer length vs. capacity.
class StringBufferDemo {
public static void main(String args[]) {
StringBuffer sb = new StringBuffer("Hello");
System.out.println("buffer = " + sb);
System.out.println("length = " + sb.length());
System.out.println("capacity = " + sb.capacity());
}
}
下面是这个程序的输出,它说明了StringBuffer如何为另外的处理预留额外的空间:
buffer = Hello
length = 5
capacity = 21
由于sb在创建时由字符串“Hello”初始化,因此它的长度为5。因为给16个附加的字符自动增加了存储空间,因此它的存储容量为21
13.10.3 ensureCapacity( )
如果想在构造StringBuffer之后为某些字符预分配空间,可以使用ensureCapacity( )方法设置缓冲区的大小。这在事先已知要在StringBuffer上追加大量小字符串的情况下是有用的。ensureCapacity( )方法具有如下的一般形式:
void ensureCapacity(int capacity)
这里capacity指定了缓冲区的大小。
13.10.4 setLength( )
使用setLength( )方法在StringBuffer对象内设置缓冲区的大小。其一般形式如下:
void setLength(int len)
这里len指定了缓冲区的长度。这个值必须是非负的。当增加缓冲区的大小时,空字符将被加在现存缓冲区的后面。如果用一个小于length( )方法返回的当前值的值调用setLength( )方法,那么在新长度之后存储的字符将被丢失。后面的setCharAtDemo例子程序使用setLength( )方法缩短StringBuffer。
13.10.5 charAt( )和setCharAt( )
使用charAt( )方法可以从StringBuffer中得到单个字符的值。可以通过setCharAt( )方法给StringBuffer中的字符置值。它们的一般形式如下:
char charAt(int where)
void setCharAt(int where, char ch)
对于charAt( )方法,where指定获得的字符的下标。对于setCharAt( )方法,where指定被置值的字符的下标,而ch指定了该字符的新值。对于这两种方法,where必须是非负的,同时不能指定在缓冲区之外的位置。
下面的例子说明了charAt( )和setCharAt( )方法:
// Demonstrate charAt() and setCharAt().
class setCharAtDemo {
public static void main(String args[]) {
StringBuffer sb = new StringBuffer("Hello");
System.out.println("buffer before = " + sb);
System.out.println("charAt(1) before = " + sb.charAt(1));
sb.setCharAt(1, 'i');
sb.setLength(2);
System.out.println("buffer after = " + sb);
System.out.println("charAt(1) after = " + sb.charAt(1));
}
}
下面是该程序的输出结果:
buffer before = Hello
charAt(1) before = e
buffer after = Hi
charAt(1) after = i
13.10.6 getChars( )
使用getChars( )方法将StringBuffer的子字符串复制给数组。其一般形式如下:
void getChars(int sourceStart, int sourceEnd, char target[ ],
int targetStart)
这里,sourceStart指定子字符串开始时的下标,而sourceEnd指定了该子字符串结束时下一个字符的下标。这意味着子字符串包含了从sourceStart到sourceEnd–1位置上的字符。接收字符的数组由target指定。在target内将被复制子字符串的位置下标由targetStart传递。应注意确保target数组足够大以便能够保存指定的子字符串所包含的字符。
13.10.7 append( )
append( )方法将任一其他类型数据的字符串形式连接到调用StringBuffer对象的后面。对所有内置的类型和Object,它都有重载形式。下面是其几种形式:
StringBuffer append(String str)
StringBuffer append(int num)
StringBuffer append(Object obj)
每个参数调用String.valueOf( )方法获得其字符串表达式。结果追加在当前StringBuffer对象后面。对每一种append( )形式,返回缓冲区本身。它允许后续的调用被连成一串,下面的例子说明了这一点:
// Demonstrate append().
class appendDemo {
public static void main(String args[]) {
String s;
int a = 42;
StringBuffer sb = new StringBuffer(40);
s = sb.append("a = ").append(a).append("!").toString();
System.out.println(s);
}
}
程序的输出如下所示:
a = 42!
当对字符串(String)对象使用+运算符时,append( )方法是最常被调用的。Java自动地改变对字符串(String)实例的修改,就像对StringBuffer实例的操作一样。因此,连接调用StringBuffer对象的append( )方法。在执行连接之后,编译器插入对toString( )方法的调用,将修改的StringBuffer返回到一个不变的字符串(String)中。所有这一切看起来是很复杂的。为什么不是仅仅只有一个其操作或多或少地像StringBuffer的字符串类呢?答案是性能。Java运行时执行的许多优化是知道字符串(String)对象是不可改变的。值得欣慰的是Java隐藏了大多数复杂的String与StringBuffer之间的转换。实际上,大多数的程序员从没有直接感觉到需要使用StringBuffer,而可以根据应用于字符串(String)变量上的+运算符表示大多数的操作。
13.10.8 insert( )
insert( )方法将一个字符串插入另一个字符串中。它被重载而接收所有简单类型的值,包括String和Object。和append( )方法一样,它调用String.valueOf( )方法得到调用它的值的字符串表达式。随后这个字符串被插入所调用的StringBuffer对象中。下面是它们的几种形式:
StringBuffer insert(int index, String str)
StringBuffer insert(int index, char ch)
StringBuffer insert(int index, Object obj)
这里index指定将字符串插入所调用的StringBuffer对象中的插入点的下标。
下面的例子程序完成在“I”和“Java”之间插入“like”的功能。
// Demonstrate insert().
class insertDemo {
public static void main(String args[]) {
StringBuffer sb = new StringBuffer("I Java!");
sb.insert(2, "like ");
System.out.println(sb);
}
}
程序的输出结果如下所示:
I like Java!
13.10.9 reverse( )
可以使用reverse( )方法将StringBuffer对象内的字符串翻转,其一般形式如下:
StringBuffer reverse( )
这种方法返回被调用对象的翻转对象。下面的程序说明了reverse( )方法:
// Using reverse() to reverse a StringBuffer.
class ReverseDemo {
public static void main(String args[]) {
StringBuffer s = new StringBuffer("abcdef");
System.out.println(s);
s.reverse();
System.out.println(s);
}
}
程序的输出结果如下所示:
abcdef
fedcba
13.10.10 delete( )和deleteCharAt( )
Java 2在StringBuffer中增加了用于删除字符串的方法delete( )和deleteCharAt( )。这些方法的一般形式如下:
StringBuffer delete(int startIndex, int endIndex)
StringBuffer deleteCharAt(int loc)
delete( )方法从调用对象中删除一串字符。这里startIndex指定了需删除的第一个字符的下标,而endIndex指定了需删除的最后一个字符的下一个字符的下标。因此要删除的子字符串从startIndex到endIndex–1,返回结果的StringBuffer对象。
deleteCharAt( )方法删除由loc指定下标处的字符,返回结果的StringBuffer对象。这里是一个说明delete( )和deleteCharAt( )方法的程序。
// Demonstrate delete() and deleteCharAt()
class deleteDemo {
public static void main(String args[]) {
StringBuffer sb = new StringBuffer("This is a test.");
sb.delete(4, 7);
System.out.println("After delete: " + sb);
sb.deleteCharAt(0);
System.out.println("After deleteCharAt: " + sb);
}
}
程序输出如下所示:
After delete: This a test.
After deleteCharAt: his a test.
13.10.11 replace( )
Java 2在StringBuffer中增加的另一个方法是replace( )。它完成在StringBuffer内部用一组字符代替另一组字符的功能。它的形式如下:
StringBuffer replace(int startIndex, int endIndex, String str)
被替换的子字符串由下标startIndex和endIndex指定。因此从startIndex到endIndex-1的子字符串被替换。替代字符串在str中传递。返回结果的StringBuffer对象。
下面的程序说明了replace( )方法:
// Demonstrate replace()
class replaceDemo {
public static void main(String args[]) {
StringBuffer sb = new StringBuffer("This is a test.");
sb.replace(5, 7, "was");
System.out.println("After replace: " + sb);
}
}
输出如下所示:
After replace: This was a test.
13.10.12 substring( )
Java 2也增加了substring( )方法,它返回StringBuffer的一部分值。它具有如下的两种形式:
String substring(int startIndex)
String substring(int startIndex, int endIndex)
第一种形式返回调用StringBuffer对象中从startIndex下标开始直至结束的一个子字符串。第二种形式返回从startIndex开始到endIndex–1结束的子字符串。这些方法与前面在String中定义的那些方法具有相同的功能。