String
是 Java 语言中内置的类,定义在 java.lang
包中。它代表字符字符串。字符串在 Java 中无处不在。仔细研究 String
类及其方法。熟练地掌握如何操作它们将对你大有裨益。Java 程序中的 字符串字面量,如“abc”,是作为此类实例实现的,如下所示
|
代码段 3.81:字符串示例。
String str = "This is string literal";
|
在右侧,创建了一个由字符串字面量表示的 String 对象。它的对象引用被分配给 str
变量。
字符串是 不可变 的;也就是说,一旦创建它们就不能修改。无论何时看起来像是修改了 String 对象,实际上都会创建一个新的 String 对象。例如,String.trim()
方法返回删除了前导和尾随空格的字符串。实际上,它会创建一个新的修剪后的字符串,然后返回它。请注意 代码段 3.82 中发生了什么
|
代码段 3.82:不可变性。
String badlyCutText = " Java is great. ";
System.out.println(badlyCutText);
badlyCutText.trim();
System.out.println(badlyCutText);
|
|
|
代码段 3.82 的输出
Java is great.
Java is great.
|
|
trim()
方法调用不会修改对象,因此不会发生任何事情。它会创建一个新的修剪后的字符串,然后丢弃它。
|
代码段 3.83:赋值。
String badlyCutText = " Java is great. ";
System.out.println(badlyCutText);
badlyCutText = badlyCutText.trim();
System.out.println(badlyCutText);
|
|
|
代码段 3.83 的输出
Java is great.
Java is great.
|
|
返回的字符串被分配给变量。它完成了任务,因为 trim()
方法创建了一个新的 String
实例。
Java 语言为使用运算符 +
连接字符串提供特殊支持
|
代码段 3.84:连接示例。
System.out.println("First part");
System.out.println(" second part");
String str = "First part" + " second part";
System.out.println(str);
|
|
|
代码段 3.84 的输出
First part
second part
First part second part
|
|
连接并不总是同时进行。 原始字符串字面量 连接在编译时完成,因此类字节码中只有一个字符串字面量。包含至少一个对象的连接在运行时完成。
+
运算符可以将其他对象与字符串连接起来。例如,整数将在连接之前转换为字符串
|
代码段 3.85:整数连接。
System.out.println("Age=" + 25);
|
|
|
代码段 3.85 的输出
Age=25
|
|
每个 Java 对象都有从 Object
类继承的 String toString()
方法。此方法提供了一种将对象转换为 String
的方法。大多数类会覆盖默认行为,以在返回的 String
中提供更具体的(更有用的)数据
|
代码段 3.86:对象连接。
System.out.println("Age=" + new Integer(31));
|
|
|
代码段 3.86 的输出
Age=31
|
|
使用 StringBuilder/StringBuffer 连接字符串
[编辑 | 编辑源代码]
请记住,String
对象是不可变对象。一旦创建了 String
,就不能修改它,它会占用内存,直到被垃圾回收。小心编写这样的方法
|
代码段 3.87:原始连接。
public String convertToString(Collection<String> words) {
String str = "";
// Loops through every element in words collection
for (String word : words) {
str = str + word + " ";
}
return str;
}
|
在 +
操作中,在每次迭代时都会创建一个新的 String
对象。假设 words
包含元素 ["Foo", "Bar", "Bam", "Baz"]
。在运行时,该方法会创建 13 个 String
""
"Foo"
" "
"Foo "
"Foo Bar"
" "
"Foo Bar "
"Foo Bar Bam"
" "
"Foo Bar Bam "
"Foo Bar Bam Baz"
" "
"Foo Bar Bam Baz "
即使实际上只有最后一个才是有用的。
为了避免像这样不必要的内存使用,请使用 StringBuilder
类。它提供与 String
相似的功能,但以可变的方式存储其数据。只创建一个 StringBuilder
对象。同样,由于对象创建很耗时,使用 StringBuilder
会生成速度更快的代码。
|
代码段 3.88:使用 StringBuilder 连接。
public String convertToString(Collection<String> words) {
StringBuilder buf = new StringBuilder();
// Loops through every element in words collection
for (String word : words) {
buf.append(word);
buf.append(" ");
}
return buf.toString();
}
|
由于 StringBuilder
不是线程安全的(请参阅有关 并发 的章节),因此你不能在多个线程中使用它。对于多线程环境,请改用 StringBuffer
,它执行相同的操作并且是线程安全的。但是,StringBuffer
速度较慢,因此只在需要时使用它。此外,在 Java 5 之前,只有 StringBuffer
存在。
比较字符串并不像乍看起来那样容易。使用 ==
比较 String
时,请注意你在做什么
|
代码段 3.89:危险的比较。
String greeting = "Hello World!";
if (greeting == "Hello World!") {
System.out.println("Match found.");
}
|
|
|
代码段 3.89 的输出
Match found.
|
|
上面和下面代码的区别在于,上面代码检查 String
是否是内存中的相同对象,它们是。这是由于 String
存储在称为字符串常量池的内存位置中的缘故。如果创建 String
时没有显式使用 new
关键字,它会检查该 String
是否已存在于池中,并使用现有的 String
。如果不存在,则会创建一个新对象。这就是 Java 中使字符串不可变的原因。要测试相等性,请使用 equals(Object)
方法,该方法由每个类继承并由 String
定义,如果且仅当传入的对象是 String
并且包含完全相同的数据时,才返回 true
|
代码段 3.90:正确的比较。
String greeting = "Hello World!";
if (greeting.equals("Hello World!")) {
System.out.println("Match found.");
}
|
|
|
代码段 3.90 的输出
Match found.
|
|
请记住,比较区分大小写。
|
代码段 3.91:使用小写进行比较。
String greeting = "Hello World!";
if (greeting.equals("hello world!")) {
System.out.println("Match found.");
}
|
|
|
代码段 3.91 的输出
|
|
要对 String
对象进行排序,请使用 compareTo()
方法,该方法可以在使用 String 数据类型的地方访问。compareTo()
方法如果参数小于、等于或大于调用它的对象,则返回负数、零或正数。让我们来看一个示例
|
代码段 3.92:顺序。
String person1 = "Peter";
String person2 = "John";
if (person1.compareTo(person2) > 0) {
// Badly ordered
String temp = person1;
person1 = person2;
person2 = temp;
}
|
代码段 3.92 正在将 String 变量 person1
与 person2
进行比较。如果 person1
即使是最细微的差别,我们也会得到一个大于或小于 0 的值,具体取决于确切的差异。如果此 String 对象在字典顺序上先于参数字符串,则结果为负。如果此 String 对象在字典顺序上后于参数字符串,则结果为正。查看 Java API 以了解更多详细信息。
有时将字符串拆分为单独的字符串(基于 正则表达式)很有用。String
类有一个 split()
方法(从 Java 1.4 开始),它将返回一个 String 数组
|
代码段 3.93:顺序。
String person = "Brown, John:100 Yonge Street, Toronto:(416)777-9999";
...
String[] personData = person.split(":");
...
String name = personData[0];
String address = personData[1];
String phone = personData[2];
|
另一个有用的应用程序可能是根据换行符来 拆分 字符串文本,以便你可以逐行处理文本。
有时创建 子字符串(或使用现有字符串中字母顺序的字符串)也很有用。这可以通过两种方法完成。
第一种方法涉及从给定索引到末尾的字符串中创建子字符串
|
代码段 3.94:截断字符串。
String str = "coffee";
System.out.println(str.substring(3));
|
|
|
代码段 3.94 的输出
fee
|
|
字符串中第一个字符的索引为 0。
从那里开始计数,很明显索引为 3 的字符是 "coffee" 中的第二个 "f"。这被称为beginIndex
。从beginIndex
到字符串结尾的所有字符都将复制到新的子字符串中。
第二种方法涉及用户定义的beginIndex
和endIndex
。
|
代码部分 3.95:字符串提取。
String str = "supporting";
System.out.println(str.substring(3, 7));
|
|
|
代码部分 3.95 的输出
port
|
|
substring()
返回的字符串将是 "port"。
s
|
u
|
p
|
p
|
o
|
r
|
t
|
i
|
n
|
g
|
0
|
1
|
2
|
3
|
4
|
5
|
6
|
7
|
8
|
9
|
请注意,endIndex
不包含在内。这意味着最后一个字符的索引将是endIndex-1
。因此,在这个例子中,从索引 3 到索引 6(包括)的所有字符都被复制到子字符串中。
|
很容易将substring() 方法误认为subString() (它不存在,编译时会返回语法错误)。子字符串被认为是一个词。这就是为什么方法名称似乎不遵循 Java 的常用语法。只要记住,这种风格只适用于由多个单词组成的的方法或其他元素。 |
String
类还允许修改大小写。实现此功能的两种方法是toLowerCase()
和toUpperCase()
。
|
代码部分 3.96:大小写修改。
String str = "wIkIbOoKs";
System.out.println(str.toLowerCase());
System.out.println(str.toUpperCase());
|
|
|
代码部分 3.96 的输出
wikibooks
WIKIBOOKS
|
|
这些方法对于进行不区分大小写的搜索非常有用。
|
代码部分 3.97:文本搜索。
String word = "Integer";
String text = "A number without a decimal part is an integer."
+ " Integers are a list of digits.";
...
// Remove the case
String lowerCaseWord = word.toLowerCase();
String lowerCaseText = text.toLowerCase();
// Search
int index = lowerCaseText.indexOf(lowerCaseWord);
while (index != -1) {
System.out.println(word
+ " appears at column "
+ (index + 1)
+ ".");
index = lowerCaseText.indexOf(lowerCaseWord, index + 1);
}
|
|
代码部分 3.97 的输出
Integer appears at column 38.
Integer appears at column 47.
|
测试您的知识
问题 3.12:您有以下形式的邮件地址:<firstName>.<lastName>@<companyName>.org
编写String getDisplayName(String)
方法,该方法接收邮件字符串作为参数,并返回可读的姓名,如下所示:LASTNAME Firstname
答案
|
答案 3.12:getDisplayName()
public static String getDisplayName(String mail) {
String displayName = null;
if (mail != null) {
String[] mailParts = mail.split("@");
String namePart = mailParts[0];
String[] namesParts = namePart.split("\\.");
// The last name
String lastName = namesParts[1];
lastName = lastName.toUpperCase();
// The first name
String firstName = namesParts[0];
String firstNameInitial = firstName.substring(0, 1);
firstNameInitial = firstNameInitial.toUpperCase();
String firstNameEnd = firstName.substring(1);
firstNameEnd = firstNameEnd.toLowerCase();
// Concatenation
StringBuilder displayNameBuilder = new StringBuilder(lastName).append(" ").append(firstNameInitial).append(firstNameEnd);
displayName = displayNameBuilder.toString();
}
return displayName;
}
|
- 我们只处理非空字符串,
- 我们首先将邮件分成两部分,将个人信息与公司信息分开,并保留姓名数据,
- 然后我们将姓名信息拆分以将名字与姓氏分开。由于
split()
方法使用正则表达式,而.
是一个通配符,因此我们需要对其进行转义(\.
)。但是,在字符串中,\
也是一个特殊字符,因此我们也需要对其进行转义(\\.
),
- 姓氏只是大写,
- 由于所有名字字符的大小写可能不一致,因此我们必须截取名字。只有名字的首字母将大写,
- 现在我们可以连接所有片段。我们更喜欢使用
StringBuilder
来做到这一点。