Skip to content

String 与字符串操作

概述

String 是 Java 中最常用的引用类型之一,表示不可变的字符序列。字符串在 JVM 中有字符串常量池(String Pool)优化,相同内容的字面量会共享同一对象。理解 String 的不可变性(immutable)以及「==equals 的区别」是正确使用字符串的关键。本节介绍 String 的创建、常用方法、拼接方式以及 StringBuilder/StringBuffer 的适用场景。

前置建议

已掌握 变量与数据类型 中的基本类型与引用类型,以及 类与对象 的基本概念。


String 的不可变性

String 在 Java 中是不可变的:一旦创建,其字符序列不能被修改。任何看似「修改」的操作(如 concatreplacetoUpperCase)都会返回新的 String 对象,而不会改变原对象。

java
String s = "hello";
s.toUpperCase();   // 返回 "HELLO",但 s 仍然是 "hello"
System.out.println(s);   // hello

String upper = s.toUpperCase();
System.out.println(upper);   // HELLO

不可变性带来的好处:线程安全、可作为 HashMap 的 key、字符串常量池可安全复用。需要频繁修改字符序列时,应使用 StringBuilder(单线程)或 StringBuffer(多线程)。


创建字符串

字面量与常量池

双引号直接写出的字符串是字面量,编译期会进入字符串常量池。相同内容的字面量在运行时会指向池中的同一对象。

java
String a = "java";
String b = "java";
// a 与 b 指向常量池中同一对象
System.out.println(a == b);   // true

使用 new String(...)

使用 new String(...) 会在堆上始终创建新对象,不会与常量池中的引用相等(除非通过 intern() 放入池并返回池中引用)。

java
String c = new String("java");
System.out.println(a == c);   // false,c 在堆上,a 在池中
System.out.println(a.equals(c));   // true,内容相同

提示

日常开发中,若无特殊需要,直接使用字面量即可,避免不必要的 new String(...)

由字符/字节构造

可从字符数组、字节数组(需指定字符集)等构造 String,便于处理二进制或逐字符逻辑。

java
char[] chars = {'J', 'a', 'v', 'a'};
String fromChars = new String(chars);   // "Java"

byte[] bytes = "Hello".getBytes(StandardCharsets.UTF_8);
String fromBytes = new String(bytes, StandardCharsets.UTF_8);   // "Hello"

基本用法与常用方法

长度与访问

方法说明
length()返回字符个数(注意与数组的 length 属性区分)
charAt(int index)返回指定索引处的字符,索引从 0 开始
isEmpty()长度为 0 时返回 true
java
String s = "Hello";
System.out.println(s.length());    // 5
System.out.println(s.charAt(0));   // H
System.out.println(s.isEmpty());   // false

比较

方法说明
equals(Object obj)内容相等返回 true,比较字符串内容必须用 equals
equalsIgnoreCase(String other)忽略大小写的比较
compareTo(String other)按字典序比较,小于/等于/大于分别返回负/0/正
startsWith(String prefix) / endsWith(String suffix)判断前缀/后缀
java
String a = "java";
String b = "Java";
System.out.println(a.equals(b));           // false
System.out.println(a.equalsIgnoreCase(b)); // true
System.out.println("abc".compareTo("abd")); // 负数(abc 字典序在前)
System.out.println("hello".startsWith("he")); // true

注意

比较字符串内容时不要用 ==== 比较的是引用是否指向同一对象;只有内容相同才应视为相等,必须使用 equals

查找与截取

方法说明
indexOf(String str) / indexOf(int ch)首次出现的位置,未找到返回 -1
lastIndexOf(String str)最后一次出现的位置
substring(int beginIndex)从 beginIndex 到末尾的子串
substring(int begin, int end)[begin, end) 左闭右开子串
java
String s = "hello world";
System.out.println(s.indexOf("o"));       // 4
System.out.println(s.lastIndexOf("o"));   // 7
System.out.println(s.substring(6));      // "world"
System.out.println(s.substring(0, 5));   // "hello"(不包含索引 5)

替换、分割与去空白

方法说明
replace(CharSequence old, CharSequence new)替换所有匹配(返回新 String)
split(String regex)按正则分割为 String[]
trim()去掉首尾空白(不含中间空白)
strip()JDK 11+,按 Unicode 空白 trim,推荐
java
String s = "  one two three  ";
System.out.println(s.trim());   // "one two three"
System.out.println("a-b-c".split("-")[1]);   // "b"
System.out.println("hello".replace("l", "L")); // "heLLo"

示例

示例 1:判断与简单处理

java
public class StringDemo {
    public static void main(String[] args) {
        String input = "  Hello, Java!  ";
        String trimmed = input.trim();
        if (trimmed.endsWith("!")) {
            String withoutExclaim = trimmed.substring(0, trimmed.length() - 1);
            System.out.println(withoutExclaim);   // "Hello, Java"
        }
        String upper = trimmed.toUpperCase();      // 对 trim 后的结果转大写,得到新对象
        System.out.println(upper);                // "HELLO, JAVA!"
    }
}

示例 2:拼接与 StringBuilder

循环中多次拼接应使用 StringBuilder,避免产生大量临时 String 对象。

java
// 不推荐:循环中用 + 拼接,每次 + 都可能产生新 String
String slow = "";
for (int i = 0; i < 100; i++) {
    slow = slow + i + ",";
}

// 推荐:使用 StringBuilder
StringBuilder sb = new StringBuilder();
for (int i = 0; i < 100; i++) {
    sb.append(i).append(",");
}
String result = sb.toString();

单次或少量拼接用 + 即可,可读性好;在循环或大量拼接时用 StringBuilder。

示例 3:按行分割与常用模式

java
String text = "line1\nline2\nline3";
String[] lines = text.split("\n");
for (String line : lines) {
    System.out.println("[" + line.trim() + "]");
}

// 判断是否包含某子串
if (text.contains("line2")) {
    System.out.println("found");
}

注意事项

  • ==equals:比较内容一律用 equals== 仅当需要「同一对象」时才用(如与字面量常量池比较时)。
  • 编码:与字节互转时务必指定字符集,如 StandardCharsets.UTF_8,避免依赖平台默认编码。
  • 性能:循环内大量拼接用 StringBuilder;多线程下需要同步时用 StringBuffer
  • null:调用方法前注意判空,避免对 null 调用 equals 导致 NPE;若需与字面量比较,推荐 "literal".equals(variable),这样 variable 为 null 时不会抛异常。

提示

"常量".equals(变量) 的写法在变量可能为 null 时更安全;若用 变量.equals("常量"),变量为 null 会抛出 NullPointerException。


相关链接

基于 VitePress 构建