跳转到内容

字符串

100% developed
来自维基教科书,开放的书籍,面向开放的世界

方法 Java 编程
字符串
类,对象和类型
浏览 语言基础 主题:v  d  e )


String 是 Java 语言中内置的类,定义在 java.lang 包中。它代表字符字符串。字符串在 Java 中无处不在。仔细研究 String 类及其方法。熟练地掌握如何操作它们将对你大有裨益。Java 程序中的 字符串字面量,如“abc”,是作为此类实例实现的,如下所示

Example 代码段 3.81:字符串示例。
String str = "This is string literal";

在右侧,创建了一个由字符串字面量表示的 String 对象。它的对象引用被分配给 str 变量。

不可变性

[编辑 | 编辑源代码]

字符串是 不可变 的;也就是说,一旦创建它们就不能修改。无论何时看起来像是修改了 String 对象,实际上都会创建一个新的 String 对象。例如,String.trim() 方法返回删除了前导和尾随空格的字符串。实际上,它会创建一个新的修剪后的字符串,然后返回它。请注意 代码段 3.82 中发生了什么

Example 代码段 3.82:不可变性。
String badlyCutText = "     Java is great.     ";
System.out.println(badlyCutText);

badlyCutText.trim();
System.out.println(badlyCutText);
Standard input or output 代码段 3.82 的输出
     Java is great.    
     Java is great.    

trim() 方法调用不会修改对象,因此不会发生任何事情。它会创建一个新的修剪后的字符串,然后丢弃它。

Example 代码段 3.83:赋值。
String badlyCutText = "     Java is great.     ";
System.out.println(badlyCutText);

badlyCutText = badlyCutText.trim();
System.out.println(badlyCutText);
Standard input or output 代码段 3.83 的输出
     Java is great.    
Java is great.

返回的字符串被分配给变量。它完成了任务,因为 trim() 方法创建了一个新的 String 实例。

Java 语言为使用运算符 + 连接字符串提供特殊支持

Example 代码段 3.84:连接示例。
System.out.println("First part");
System.out.println(" second part");
String str = "First part" + " second part";
System.out.println(str);
Standard input or output 代码段 3.84 的输出
First part
 second part
First part second part

连接并不总是同时进行。 原始字符串字面量 连接在编译时完成,因此类字节码中只有一个字符串字面量。包含至少一个对象的连接在运行时完成。

+ 运算符可以将其他对象与字符串连接起来。例如,整数将在连接之前转换为字符串

Example 代码段 3.85:整数连接。
System.out.println("Age=" + 25);
Standard input or output 代码段 3.85 的输出
Age=25

每个 Java 对象都有从 Object 类继承的 String toString() 方法。此方法提供了一种将对象转换为 String 的方法。大多数类会覆盖默认行为,以在返回的 String 中提供更具体的(更有用的)数据

Example 代码段 3.86:对象连接。
System.out.println("Age=" + new Integer(31));
Standard input or output 代码段 3.86 的输出
Age=31

使用 StringBuilder/StringBuffer 连接字符串

[编辑 | 编辑源代码]

请记住,String 对象是不可变对象。一旦创建了 String,就不能修改它,它会占用内存,直到被垃圾回收。小心编写这样的方法

Example 代码段 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

  1. ""
  2. "Foo"
  3. " "
  4. "Foo "
  5. "Foo Bar"
  6. " "
  7. "Foo Bar "
  8. "Foo Bar Bam"
  9. " "
  10. "Foo Bar Bam "
  11. "Foo Bar Bam Baz"
  12. " "
  13. "Foo Bar Bam Baz "

即使实际上只有最后一个才是有用的。

为了避免像这样不必要的内存使用,请使用 StringBuilder 类。它提供与 String 相似的功能,但以可变的方式存储其数据。只创建一个 StringBuilder 对象。同样,由于对象创建很耗时,使用 StringBuilder 会生成速度更快的代码。

Example 代码段 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 时,请注意你在做什么

Example 代码段 3.89:危险的比较。
String greeting = "Hello World!";
if (greeting == "Hello World!") {
    System.out.println("Match found.");
}
Standard input or output 代码段 3.89 的输出
Match found.

上面和下面代码的区别在于,上面代码检查 String 是否是内存中的相同对象,它们是。这是由于 String 存储在称为字符串常量池的内存位置中的缘故。如果创建 String 时没有显式使用 new 关键字,它会检查该 String 是否已存在于池中,并使用现有的 String。如果不存在,则会创建一个新对象。这就是 Java 中使字符串不可变的原因。要测试相等性,请使用 equals(Object) 方法,该方法由每个类继承并由 String 定义,如果且仅当传入的对象是 String 并且包含完全相同的数据时,才返回 true

Example 代码段 3.90:正确的比较。
String greeting = "Hello World!";
if (greeting.equals("Hello World!")) {
    System.out.println("Match found.");
}
Standard input or output 代码段 3.90 的输出
Match found.

请记住,比较区分大小写。

Example 代码段 3.91:使用小写进行比较。
String greeting = "Hello World!";
if (greeting.equals("hello world!")) {
    System.out.println("Match found.");
}
Standard input or output 代码段 3.91 的输出

要对 String 对象进行排序,请使用 compareTo() 方法,该方法可以在使用 String 数据类型的地方访问。compareTo() 方法如果参数小于、等于或大于调用它的对象,则返回负数、零或正数。让我们来看一个示例

Example 代码段 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 变量 person1person2 进行比较。如果 person1 即使是最细微的差别,我们也会得到一个大于或小于 0 的值,具体取决于确切的差异。如果此 String 对象在字典顺序上先于参数字符串,则结果为负。如果此 String 对象在字典顺序上后于参数字符串,则结果为正。查看 Java API 以了解更多详细信息。

拆分字符串

[编辑 | 编辑源代码]

有时将字符串拆分为单独的字符串(基于 正则表达式)很有用。String 类有一个 split() 方法(从 Java 1.4 开始),它将返回一个 String 数组

Example 代码段 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];

另一个有用的应用程序可能是根据换行符来 拆分 字符串文本,以便你可以逐行处理文本。

子字符串

[编辑 | 编辑源代码]

有时创建 子字符串(或使用现有字符串中字母顺序的字符串)也很有用。这可以通过两种方法完成。

第一种方法涉及从给定索引到末尾的字符串中创建子字符串

Example 代码段 3.94:截断字符串。
String str = "coffee";
System.out.println(str.substring(3));
Standard input or output 代码段 3.94 的输出
fee

字符串中第一个字符的索引为 0。

c o f f e e
0 1 2 3 4 5

从那里开始计数,很明显索引为 3 的字符是 "coffee" 中的第二个 "f"。这被称为beginIndex。从beginIndex到字符串结尾的所有字符都将复制到新的子字符串中。

第二种方法涉及用户定义的beginIndexendIndex

Example 代码部分 3.95:字符串提取。
String str = "supporting";
System.out.println(str.substring(3, 7));
Standard input or output 代码部分 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(包括)的所有字符都被复制到子字符串中。

Note 很容易将substring()方法误认为subString()(它不存在,编译时会返回语法错误)。子字符串被认为是一个词。这就是为什么方法名称似乎不遵循 Java 的常用语法。只要记住,这种风格只适用于由多个单词组成的的方法或其他元素。

字符串大小写

[编辑 | 编辑源代码]

String类还允许修改大小写。实现此功能的两种方法是toLowerCase()toUpperCase()

Example 代码部分 3.96:大小写修改。
String str = "wIkIbOoKs";
System.out.println(str.toLowerCase());
System.out.println(str.toUpperCase());
Standard input or output 代码部分 3.96 的输出
wikibooks
WIKIBOOKS

这些方法对于进行不区分大小写的搜索非常有用。

Example 代码部分 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);
}
Standard input or output 代码部分 3.97 的输出
Integer appears at column 38.
Integer appears at column 47.
测试您的知识

问题 3.12:您有以下形式的邮件地址:<firstName>.<lastName>@<companyName>.org

编写String getDisplayName(String)方法,该方法接收邮件字符串作为参数,并返回可读的姓名,如下所示:LASTNAME Firstname

答案
Example 答案 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;
}
  1. 我们只处理非空字符串,
  2. 我们首先将邮件分成两部分,将个人信息与公司信息分开,并保留姓名数据,
  3. 然后我们将姓名信息拆分以将名字与姓氏分开。由于split()方法使用正则表达式,而.是一个通配符,因此我们需要对其进行转义(\.)。但是,在字符串中,\也是一个特殊字符,因此我们也需要对其进行转义(\\.),
  4. 姓氏只是大写,
  5. 由于所有名字字符的大小写可能不一致,因此我们必须截取名字。只有名字的首字母将大写,
  6. 现在我们可以连接所有片段。我们更喜欢使用StringBuilder来做到这一点。

另请参见

[编辑 | 编辑源代码]
方法 Java 编程
字符串
类,对象和类型
华夏公益教科书