字符串 String
掌握 String 不可变性、字符串池、常用方法、StringBuilder
字符串 String
String 是 Java 最常用的类,但它和普通类有个关键区别:String 是不可变的。理解这一点,才能写出高效的字符串代码。
学完本章你将: 掌握 String 不可变性原理、字符串池机制、常用 API、StringBuilder 高效拼接。
String 不可变性
String 内部用 private final byte[] 存储字符,创建后无法修改任何内容。所有看似"修改"字符串的操作(toUpperCase、trim、+ 拼接),实际上都在创建新的 String 对象。
String s1 = "Hello";
String s2 = s1.toUpperCase(); // 不是修改 s1,而是创建新 String
System.out.println(s1); // "Hello" —— 原字符串纹丝不动
System.out.println(s2); // "HELLO"
String str = "Java";
str = str + "Script"; // 又是创建新对象,str 指向了新地址
为什么要设计成不可变? 主要三个原因:
- 安全:字符串被广泛用作参数(文件路径、网络 URL),不可变意味着不会被意外篡改
- 线程安全:多线程随便读,不需要加锁
- 字符串池:相同的字符串字面量可以共用同一对象,节省内存
字符串池
JVM 维护了一个「字符串常量池」。当你用双引号 "hello" 创建字符串时,JVM 先去池里找——有就直接返回,没有才创建并放入池中。
String a = "hello";
String b = "hello";
System.out.println(a == b); // true!a 和 b 指向池中同一个对象
但 new String() 绕过了池,强制在堆上创建新对象:
String c = new String("hello");
System.out.println(a == c); // false!c 是堆上的新对象
System.out.println(a.equals(c)); // true ✅ —— 比较内容要用 equals!
intern() 可以手动把堆上的字符串"请入池":
String d = c.intern(); // 尝试入池,若已存在则返回池中的
System.out.println(a == d); // true
⚠️ 黄金法则: 比较字符串内容永远用
equals(),不要用==。==比较的是内存地址。
常用方法
String s = " Hello World! ";
// —— 基本信息 ——
s.length(); // 16(含空格)
s.charAt(6); // 'H'(索引从 0 开始)
// —— 查找 ——
s.contains("World"); // true
s.indexOf("o"); // 5(第一次出现的位置)
s.lastIndexOf("o"); // 9(最后一次出现)
// —— 提取(都不改变原字符串) ——
s.trim(); // "Hello World!"(去首尾空格)
s.substring(2, 7); // "Hello"(左闭右开:[2,7))
s.replace("l", "L"); // " HeLLo WorLd! "
// —— 分割与拼接 ——
String[] parts = "a,b,c".split(","); // ["a","b","c"]
String.join("-", parts); // "a-b-c"
// —— 判断 ——
s.startsWith(" H"); // true
s.endsWith("! "); // true
s.isBlank(); // false(有内容)
" ".isBlank(); // true(只有空白)
"".isEmpty(); // true(长度为 0)
StringBuilder —— 高效拼接
循环中每次 + 都会创建新 String 对象,如果循环 1000 次就是 1000 个临时对象,严重浪费内存和 CPU。
// ❌ 低效:1000 个临时 String 对象
String result = "";
for (int i = 0; i < 1000; i++) {
result += i; // 每次 += 都创建新对象!
}
// ✅ 高效:内部用可变的 byte[] 数组,只创建一个对象
StringBuilder sb = new StringBuilder();
for (int i = 0; i < 1000; i++) {
sb.append(i); // 往同一个数组后追加
}
String result2 = sb.toString();
StringBuilder 常用操作:
StringBuilder sb = new StringBuilder();
sb.append("Hello");
sb.append(" World");
sb.insert(5, " Java"); // 在位置 5 插入 → "Hello Java World"
sb.delete(0, 6); // 删除 [0,6) → "Java World"
sb.reverse(); // 反转
sb.toString(); // 最后转回 String
💡 选择: 单次拼接用
+(编译器会自动优化为 StringBuilder),循环拼接用StringBuilder,多线程用StringBuffer(线程安全但慢)。
StringBuilder sb = new StringBuilder(); for (int i = 0; i < 1000; i++) { sb.append(i); } String result2 = sb.toString();
// 常用操作 sb.append("Hello") .append(" ") .append("World"); sb.insert(5, " Java"); sb.delete(0, 6); sb.reverse();