Skip to content

Java 字符串

在 Java 中,String 是基础且重要的,用于表示字符序列。与原始类型不同,Java 中的 String 是由 java.lang.String 类提供的对象。

本教程将使用现代 Java 实践探索如何创建和操作 String。

最常用且推荐的创建 String 的方法是使用 String 字面量:

// 将引用分配给字符串常量池中现有或新的 String 对象。
String greeting = "Hello, world!";

当编译器遇到一个 String 字面量时,它会创建一个 String 对象。为了提高效率,Java 维护了一个特殊的内存区域,称为“String Pool”(字符串常量池)。如果常量池中已存在相同的字面量,则会重用现有对象的引用。

你也可以从字符数组创建 String:

public class StringCreationDemo {
public static void main(String[] args) {
char[] helloArray = { 'H', 'e', 'l', 'l', 'o', '.' };
String helloString = new String(helloArray);
System.out.println(helloString); // 输出: Hello.
}
}

注意:虽然你可以使用 new String(“literal”),但在大多数情况下通常不推荐这样做,因为它会绕过 String Pool,不必要地创建一个新对象。

对于多行 String (Java 15+),使用 Text Blocks 更清晰:

String html = """
<html>
<body>
<p>Hello, world</p>
</body>
</html>
""";
System.out.println(html);

Java 中一个重要的概念是 String 对象是 immutable(不可变的)。这意味着一旦创建了 String 对象,其内部状态(字符序列)就不能更改。任何看似修改 String 的方法(如 toUpperCase() 或 substring())实际上都会创建并返回一个包含修改后内容的 new String 对象,而原始 String 则保持不变。

为什么是不可变的?它提供了几个优点:

  • **线程安全 (Thread Safety):**不可变对象可以在多个线程之间安全地共享,无需同步。
  • 安全性 (Security): String 参数(如文件路径或 URL)在验证后不会意外地被更改。
  • **性能 (Performance):**不可变性允许进行优化,例如 String Pool(缓存相同的 String 字面量)。

当你需要对字符序列进行许多修改(例如,在循环内构建一个 String)时,创建大量中间的 String 对象可能会效率低下。对于这些场景,Java 提供了可变类:

  • **StringBuilder:**在单线程环境中进行可变 String 操作的首选。它通常更快。
  • StringBuffer: StringBuilder 的线程安全替代品。只有当你确实需要线程安全时才使用它,因为它会带来性能开销。

使用 StringBuilder 的示例:

StringBuilder builder = new StringBuilder();
builder.append("Hello, ");
builder.append("world!");
builder.append(" The year is ");
builder.append(2024);
String result = builder.toString(); // 转换回一个不可变的 String 对象
System.out.println(result); // 输出: Hello, world! The year is 2024

length() 方法返回 String 中的字符数。

public class StringLengthDemo {
public static void main(String[] args) {
String palindrome = "Dot saw I was Tod";
int len = palindrome.length();
System.out.println("String Length is: " + len); // 输出: String Length is: 17
}
}

最常用的拼接 String 的方法是使用 + 运算符。

String firstName = "Ada";
String lastName = "Lovelace";
String fullName = firstName + " " + lastName; // "Ada Lovelace"
System.out.println(fullName);

对于简单的情况,+ 运算符很方便。但是,在循环中使用 + 拼接 String 可能会效率低下,因为它可能创建许多中间的 String 对象。在循环中为了更好的性能,请使用 StringBuilder:

String[] words = {"Java", "is", "fun"};
StringBuilder sentence = new StringBuilder();
for (String word : words) {
sentence.append(word).append(" ");
}
String finalSentence = sentence.toString().trim(); // "Java is fun"
System.out.println(finalSentence);

String 类也有一个 concat(String str) 方法,但通常更推荐使用 + 运算符和 StringBuilder。

静态方法 String.format() 允许创建格式化 String,类似于 C/C++ 中的 printf。

double floatVar = 3.14159;
int intVar = 42;
String stringVar = "Example";
String fs = String.format("Float: %.2f, Integer: %d, String: %s",
floatVar, intVar, stringVar);
System.out.println(fs);
// 输出: Float: 3.14, Integer: 42, String: Example

自 Java 15 起,你还可以使用 formatted() 实例方法,这与 text blocks 配合使用通常很方便:

String name = "World";
int year = 2024;
String formattedString = """
Hello, %s!
Welcome to %d.
""".formatted(name, year);
System.out.println(formattedString);

String 类提供了一套丰富的方法。这里列出了一些最常用的方法,按功能分组:

  • int length(): 返回字符数。示例:“Java”.length() // 4
  • boolean isEmpty(): 如果长度为 0,则返回 true。示例:"".isEmpty() // true
  • boolean isBlank() (Java 11+): 如果 String 为空或只包含空白字符,则返回 true。示例:” “.isBlank() // true
  • boolean equals(Object obj): 比较 String 内容是否相等(区分大小写)。重点:比较 String 内容请使用此方法,不要使用 ==。
  • boolean equalsIgnoreCase(String anotherString): 比较 String 内容,忽略大小写。
  • int compareTo(String anotherString): 按字典顺序比较 String(如果相等则返回 0,如果当前 String 在前面则返回 <0,如果 anotherString 在前面则返回 >0)。对于排序很有用。
  • int compareToIgnoreCase(String str): 按字典顺序比较,忽略大小写。
  • boolean contains(CharSequence s): 检查 String 是否包含指定的字符序列。示例:“Hello”.contains(“ell”) // true
  • int indexOf(int ch / String str): 返回第一次出现的索引,如果未找到则返回 -1。可以从指定索引开始搜索:indexOf(…, int fromIndex)。
  • int lastIndexOf(int ch / String str): 返回最后一次出现的索引,如果未找到则返回 -1。可以从指定索引开始向后搜索:lastIndexOf(…, int fromIndex)。
  • boolean startsWith(String prefix): 检查 String 是否以指定前缀开头。示例:“Java”.startsWith(“Ja”) // true
  • boolean endsWith(String suffix): 检查 String 是否以指定后缀结尾。示例:“Java”.endsWith(“va”) // true
  • String substring(int beginIndex): 返回从 beginIndex 到结尾的子 String。
  • String substring(int beginIndex, int endIndex): 返回从 beginIndex 到 endIndex(不包含 endIndex)的子 String。示例:“Example”.substring(1, 4) // “xam”
  • char charAt(int index): 返回指定索引处(基于 0)的字符。示例:“Java”.charAt(0) // ‘J’
  • char[] toCharArray(): 将 String 转换为新的字符数组。

操作 (大小写、空白字符、替换):

Section titled “操作 (大小写、空白字符、替换):”
  • String toLowerCase(): 将 String 转换为小写。
  • String toUpperCase(): 将 String 转换为大写。
  • String trim(): 移除开头和结尾的空白字符(基于传统空白字符定义)。
  • String strip() (Java 11+): 移除开头和结尾的空白字符(支持 Unicode)。优于 trim()。
  • String stripLeading() / stripTrailing() (Java 11+): 只移除开头或结尾的空白字符。
  • String replace(char oldChar, char newChar): 将所有出现的 oldChar 替换为 newChar。
  • String replace(CharSequence target, CharSequence replacement): 替换所有出现的 target 字符序列。
  • String replaceAll(String regex, String replacement): 替换匹配正则表达式的子 String。
  • String replaceFirst(String regex, String replacement): 替换第一个匹配正则表达式的子 String。
  • String repeat(int count) (Java 11+): 返回将当前 String 重复 count 次的新 String。
  • String[] split(String regex): 根据给定的正则表达式匹配项分割 String。示例:“a,b,c”.split(”,”) // {“a”, “b”, “c”}
  • static String join(CharSequence delimiter, CharSequence… elements) (Java 8+): 使用分隔符拼接元素。示例:String.join(”-”, “a”, “b”, “c”) // “a-b-c”
  • static String join(CharSequence delimiter, Iterable<? extends CharSequence> elements) (Java 8+): 使用分隔符拼接集合中的元素。
  • byte[] getBytes(Charset charset): 使用指定的字符集将 String 编码为字节序列。**最佳实践:**始终指定 Charset,例如 StandardCharsets.UTF_8。
  • byte[] getBytes(): 使用平台的默认字符集进行编码。**警告:**避免使用此方法,因为默认字符集在不同系统之间可能变化,导致潜在的 bug。请改用带显式 Charset 的版本。

此列表涵盖了许多基本方法。有关完整概述和高级方法,请查阅 java.lang.String 类的官方 Java API 文档。