
6.1 String类
字符串是字符的序列,是许多程序设计语言的基本数据结构。有些语言中的字符串是通过字符数组实现的(如C语言),Java语言是通过字符串类实现的。Java语言提供了三个字符串类:String类、StringBuilder类和StringBuffer类。
String类是不变字符串,StringBuilder和StringBuffer是可变字符串,这三种字符串都是16位的Unicode字符序列,并且这三个类都被声明为final,因此不能被继承。这三个类各有不同的特点,应用于不同场合。
String类是最常用的字符串类。在前面章节中已多次使用字符串对象。
6.1.1 创建String类对象
在Java程序中,有一种特殊的创建String对象的方法,直接利用字符串字面值创建字符串对象。例如:

一般使用String类的构造方法创建一个字符串对象。String类有十多个重载的构造方法,可以生成一个空字符串,也可以由字符或字节数组生成字符串。常用的构造方法如下:
- public String():创建一个空字符串。
- public String(char[] value):使用字符数组中的字符创建字符串。
- public String(char[] value, int offset, int count):使用字符数组中offset为起始下标,count个字符创建一个字符串。
- public String(byte[] bytes, String charsetName):使用指定的字节数组构造一个新的字符串,新字符串的长度与字符集有关,因此可能与字节数组的长度不同。charsetName为使用的字符集名,如US-ASCII、ISO-8859-1、UTF-8、UTF-16等。如果使用了系统不支持的字符集,将抛出UnsupportedEncodingException异常。
- public String(String original):使用一个字符串对象创建字符串。
- public String(StringBuffer buffer):使用StringBuffer对象创建字符串。
- public String(StringBuilder buffer):使用StringBuilder对象创建字符串。
下面代码说明了使用字符串的构造方法创建字符串对象。

6.1.2 字符串基本操作
首先看字符串在内存的表示。假设有下面声明:

该字符串对象在内存中的状态如图6-1所示。

图6-1 字符串对象的内存表示
该字符串共包含12个字符,即长度为12,其中每个字符都有一个下标,下标从0开始,可以通过下标访问每个字符。可以调用String类的方法操作字符串,下面是几个最常用方法。
- public int length():返回字符串的长度,即字符串包含的字符个数。注意,对含有中文或其他语言符号的字符串,计算长度时,一个符号作为一个字符计数。
- public String substring(int beginIndex, int endIndex):从字符串的下标beginIndex开始到endIndex结束产生一个子字符串。
- public String substring(int beginIndex):从字符串的下标beginIndex开始到结束产生一个子字符串。
- public String toUpperCase():将字符串转换成大写字母。
- public String toLowerCase():将字符串转换成小写字母。
- public String trim():返回删除了前后空白字符的字符串对象。
- public boolean isEmpty():返回该字符串是否为空(""),如果length()的结果为0,方法返回true,否则返回false。
- public String concat(String str):将调用字符串与参数字符串连接起来,产生一个新的字符串。
- public String replace(char oldChar, char newChar):将字符串中的所有oldChar字符改变为newChar字符,返回一个新的字符串。
- public char charAt(int index):返回字符串中指定位置的字符,index表示位置,为0~s.length()−1。
- public static String valueOf(double d):将参数的基本类型double值转换为字符串。String类中还定义了其他多个重载的valueOf()方法。
下面代码演示了String类的几个方法的使用。

下面程序要求从键盘输入一个字符串,判断该字符串是否是回文串。一个字符串,如果从前向后读和从后向前读都一样,则称该串为回文串。例如,“mom”和“上海海上”都是回文串。
对于一个字符串,先判断该字符串的第一个字符和最后一个字符是否相等,如果相等,检查第二个字符和倒数第二个字符是否相等。这个过程一直进行,直到出现不相等的情况或串中所有字符都检测完毕。当字符串有奇数个字符时,中间的字符不用检查。
程序6.1 Palindrome.java

6.1.3 字符串查找
String类提供了从字符串中查找字符和子串的方法,如下所示。
- public int indexOf(int ch):查找字符ch第一次出现的位置。如果查找不成功则返回−1,下述方法相同。
- public int indexOf(int ch, int fromIndex ):查找字符ch从fromIndex开始第一次出现的位置(在原字符串中的下标)。
- public int indexOf(String str):查找字符串str第一次出现的位置。
- public int indexOf(String str, int fromIndex ):查找字符串str从fromIndex开始第一次出现的位置(在原字符串中的下标)。
- public int lastIndexOf(int ch):查找字符ch最后一次出现的位置。
- public int lastIndexOf(int ch, int endIndex):查找字符ch到endIndex为止最后一次出现的位置。
- public int lastIndexOf(String str):查找字符串str最后一次出现的位置。
- public int lastIndexOf(String str, int endIndex):查找字符串str到endIndex为止最后一次出现的位置(在原字符串中的下标)。
下列代码演示了几个查找方法:

6.1.4 字符串转换为数组
字符串不是数组,但是字符串能够转换成字符数组或字节数组。String类提供了下列方法将字符串转换成数组。
- public char[] toCharArray():将字符串中的字符转换为字符数组。
- public void getChars(int srcBegin, int srcEnd, char[] dst, int dstBegin):将字符串中从起始位置(srcBegin)到结束位置(srcEnd)之间的字符复制到字符数组dst中,dstBegin为目标数组的起始位置。
- public byte[] getBytes():使用平台默认的字符集将字符串编码成字节序列,并将结果存储到字节数组中。
- public byte[] getBytes(String charsetName):使用指定的字符集将字符串编码成字节序列,并将结果存储到字节数组中。该方法抛出java.io.UnsupportedEncodingException异常。
下面代码使用toCharArray()方法将字符串转换为字符数组,使用getChars()方法将字符串的一部分复制到字符数组中。

6.1.5 字符串比较
在Java程序中,经常需要比较两个字符串是否相等或比较两个字符串的大小。
1.比较字符串相等
如果要比较两个字符串对象的内容是否相等,可以使用String类的equals()方法或equalsIgnoreCase()方法。
- public boolean equals(String anotherString):比较两个字符串内容是否相等。
- public boolean equalsIgnoreCase(String anotherString):比较两个字符串内容是否相等,不区分大小写。
下面代码演示了这两个方法的使用。

实际上equals()是从Object类中继承来的,原来在Object类中,该方法比较的是对象的引用,而在String类中覆盖了该方法,比较的是字符串的内容。
特别注意,不能使用“==”号来比较字符串内容是否相等,请看下面代码。

这是因为在使用“==”比较引用类型的数据(对象)时,比较的是引用(地址)是否相等。只有两个引用指向同一个对象时,结果才为true。上面使用构造方法创建的两个对象是不同的,因此s1和s2的引用是不同的,如图6-2(a)所示。
再请看下面一段代码:

这次输出结果为true。这两段代码的不同之处在于创建s1和s2对象的代码不同。这里的s1和s2是用字符串常量创建的两个对象。字符串常量存储和对象存储不同,字符串常量是存储在常量池中,对内容相同的字符串常量在常量池中只有一个副本,因此s1和s2是指向同一个对象,如图6-2(b)所示。

图6-2 字符串对象与字符串常量的不同
2.比较字符串的大小
使用equals()方法只能比较字符串相等与否,要比较大小,可以使用String类的compareTo()方法,格式为:

该方法将当前字符串与参数字符串比较,并返回一个整数值。使用字符的Unicode码值进行比较。若当前字符串小于参数字符串,方法返回值小于0;若当前字符串大于参数字符串,方法返回值大于0;若当前字符串等于参数字符串,方法返回值等于0。
下面语句输出−2,因为'C'的Unicode值比'E'的Unicode值小2。

如果在字符串比较时忽略大小写,可使用compareToIgnoreCase(String anotherString)方法。
注意:字符串不能使用>、>=、<、<=进行比较,要比较大小只能使用compareTo()方法。但可以使用==和!=比较两个字符串,但它们比较的是两个字符串引用是否相同。
下面的例子使用起泡排序法,将给出的字符串数组按由小到大的顺序排序。
程序6.2 StringSort.java

程序输出结果为:

String类还提供了下面方法判断一个字符串是否以某个字符串开头或结尾或为另一个字符串的子串。
- public boolean startsWith(String prefix):返回字符串是否以某个字符串开始。
- public boolean endsWith(String suffix):返回字符串是否以某个字符串结尾。
- public boolean contains(String str):如果参数字符串str是当前字符串的子串,则返回true。
另外,String类还提供了regionMatches()方法比较两个字符串指定区域的字符串是否相等。
6.1.6 字符串的拆分与组合
使用String类的split()方法可以将一个字符串分解成子字符串或令牌(token),使用join()方法可以将String数组中字符串连接起来,使用matches()方法返回字符串是否与正则表达式匹配。
- public String[] split(String regex):参数regex表示正则表达式,根据给定的正则表达式将字符串拆分成字符串数组。
- public boolean matches(String regex):返回字符串是否与给定的正则表达式匹配。
- public static String join(CharSequence delimiter, CharSequence…elements):使用指定的分隔符将elements的各元素组合成一个新字符串。
下面代码拆分一个字符串。

在split()中指定的正则表达式“[ ,.]”的含义是使用空格、逗号或点为分隔符拆分字符串。
String类的静态join()方法,实现将一个字符串数组按指定的分割符组合成一个字符串,它的功能与split()方法相反。下面代码演示了join()方法的使用。

6.1.7 String对象的不变性
在Java程序中,一旦创建一个String对象,就不能对其内容进行改变,因此说Java的String对象是不可变的字符串。
有些方法看起来是修改字符串,但字符串修改后产生了另一个字符串,这些方法对原字符串没有任何影响,原字符串永远不会改变。请看下面的例子。

代码运行结果为:

6.1.8 命令行参数
Java应用程序从main()方法开始执行,main()方法的声明格式为:

参数String[] args称为命令行参数,是一个字符串数组,该参数是在程序运行时通过命令行传递给main()方法。
下面程序要求从命令行为程序传递三个参数,在main()方法中通过args[0]、args[1]、args[2]输出这三个参数的值。
程序6.3 HelloProgram.java

运行该程序需要通过命令行为程序传递三个参数。例如:

程序运行结果为:

在命令行中参数字符串是通过空格分隔的。如果参数本身包含空格,则需要用双引号将参数括起来。
Java解释器根据传递的参数个数确定数组args的长度,如果给出的参数少于引用的元素,则抛出ArrayIndexOutOfBoundsException运行时异常。例如:

上述命令中只提供了两个命令行参数,创建的args数组长度为2,而程序中访问了第三个元素(args[2]),故产生运行时异常。
命令行参数传递的是字符串,若将其作为数值处理,需要进行转换。例如,可以使用Integer类的parseInt()方法将参数转换为int类型的数据。