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
|
|
请记住,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 常量池的地方。如果在创建 String
时没有显式使用 new
关键字,它会检查它是否已存在于池中,并使用现有的那个。如果不存在,则会创建一个新的对象。这就是允许字符串在 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];
|
另一个有用的应用可能是根据换行符拆分 String 文本,以便您可以逐行处理文本。
有时,使用现有字符串中的字母顺序创建子字符串或字符串也可能很有用。这可以通过两种方法完成。
第一种方法涉及从给定索引到结尾从字符串的字符中创建一个子字符串
|
代码部分 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
来完成这个操作。