String 与字符串操作
概述
String 是 Java 中最常用的引用类型之一,表示不可变的字符序列。字符串在 JVM 中有字符串常量池(String Pool)优化,相同内容的字面量会共享同一对象。理解 String 的不可变性(immutable)以及「== 与 equals 的区别」是正确使用字符串的关键。本节介绍 String 的创建、常用方法、拼接方式以及 StringBuilder/StringBuffer 的适用场景。
String 的不可变性
String 在 Java 中是不可变的:一旦创建,其字符序列不能被修改。任何看似「修改」的操作(如 concat、replace、toUpperCase)都会返回新的 String 对象,而不会改变原对象。
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(多线程)。
创建字符串
字面量与常量池
双引号直接写出的字符串是字面量,编译期会进入字符串常量池。相同内容的字面量在运行时会指向池中的同一对象。
String a = "java";
String b = "java";
// a 与 b 指向常量池中同一对象
System.out.println(a == b); // true使用 new String(...)
使用 new String(...) 会在堆上始终创建新对象,不会与常量池中的引用相等(除非通过 intern() 放入池并返回池中引用)。
String c = new String("java");
System.out.println(a == c); // false,c 在堆上,a 在池中
System.out.println(a.equals(c)); // true,内容相同提示
日常开发中,若无特殊需要,直接使用字面量即可,避免不必要的 new String(...)。
由字符/字节构造
可从字符数组、字节数组(需指定字符集)等构造 String,便于处理二进制或逐字符逻辑。
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 |
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) | 判断前缀/后缀 |
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) 左闭右开子串 |
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,推荐 |
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:判断与简单处理
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 对象。
// 不推荐:循环中用 + 拼接,每次 + 都可能产生新 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:按行分割与常用模式
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。
相关链接
- 常用类(Object、equals/hashCode、包装类、Math)
- 正则表达式(与
split、replaceAll、matches配合) - Oracle Java 文档 - String