Java 字符串
字符串是 Java 中用得最多的引用类型——日志、接口、配置、用户输入,到处都是 String。本章带你彻底搞懂 Java 字符串的核心:不可变性、常用方法、StringBuilder、String.format,以及新手最常踩的 == vs equals 陷阱。
1. 创建字符串:& == vs equals 的陷阱
String 是引用类型,创建方式有几种。最关键的是理解字符串常量池和 == vs equals 的区别:
public class StringCreate {
public static void main(String[] args) {
// 1. 直接量(最常用,放进字符串常量池)
String s1 = "Hello";
// 2. new 创建(在堆上新建对象)
String s2 = new String("Hello");
// 3. 从字符数组构造
char[] chars = {'J', 'a', 'v', 'a'};
String s3 = new String(chars);
// 4. 从字节数组构造(网络/文件场景常用)
byte[] bytes = {72, 105};
String s4 = new String(bytes); // "Hi"
System.out.println(s1 + " " + s2 + " " + s3 + " " + s4);
// ⚠️ String 是引用类型,但 == 比较的是地址,不是内容!
String a = "hi";
String b = "hi";
String c = new String("hi");
System.out.println(a == b); // true (常量池复用)
System.out.println(a == c); // false(堆上新建)
System.out.println(a.equals(c)); // true (内容相同)
}
}新手雷区:== 比较的是地址(两个引用是否指向同一对象),equals 比较的是内容。字符串内容比较永远用 .equals()!IDEA 会在 str1 == str2 处报警告,要养成习惯。
2. String 的不可变性(Immutable)
Java 中 String 对象一旦创建,内容就不可变。所有"修改"方法(replace、substring、toLowerCase)都是返回新字符串,不改原对象:
public class Immutable {
public static void main(String[] args) {
String s = "Hello";
String s2 = s.replace("H", "J");
System.out.println(s); // Hello(原字符串不变!)
System.out.println(s2); // Jello
// s 和 s2 是两个不同的对象
// 字符串拼接也是创建新对象
String a = "a" + "b" + "c"; // 编译期优化成 "abc"
// 循环里拼接会创建大量中间对象,这就是为什么要用 StringBuilder
}
}不可变的好处:线程安全(多线程放心共享)、可以做 Map 的 key(hashCode 不会变)、常量池复用省内存。代价是大量拼接时性能差——下面用 StringBuilder 解决。
3. 常用方法(必须熟练)
String 有几十个方法,日常 80% 用到的是下面这些:
public class StringMethods {
public static void main(String[] args) {
String s = "Hello, Java";
// 长度
System.out.println(s.length()); // 11
// 取字符
System.out.println(s.charAt(0)); // H
// 子串:beginIndex(包含) 到 endIndex(不包含)
System.out.println(s.substring(7)); // Java
System.out.println(s.substring(0, 5)); // Hello
// 查找
System.out.println(s.indexOf("Java")); // 7
System.out.println(s.indexOf("x")); // -1(找不到)
System.out.println(s.contains("Java")); // true
// 大小写
System.out.println(s.toUpperCase()); // HELLO, JAVA
System.out.println(s.toLowerCase()); // hello, java
// 替换
System.out.println(s.replace("Java", "World")); // Hello, World
// 去掉首尾空白
String padded = " hi ";
System.out.println(padded.trim()); // "hi"
System.out.println(padded.strip()); // Java 11+,支持 Unicode 空白
// 分割
String csv = "a,b,c,d";
String[] parts = csv.split(",");
for (String p : parts) System.out.println(p); // a b c d
// 比较时忽略大小写
System.out.println("ABC".equalsIgnoreCase("abc")); // true
// 判空
System.out.println(s.isEmpty()); // false(length == 0)
System.out.println(s.isBlank()); // false(Java 11+,全空白也算空)
}
}记忆技巧:Java 的 length() 是方法(带括号),而数组的 length 是属性(不带括号)——这是常见混淆点。
4. StringBuilder(循环拼接必用)
在循环里用 + 拼接字符串性能很差(每次都新建对象),正确做法是用 StringBuilder:
public class StringBuilderDemo {
public static void main(String[] args) {
// ⚠️ 在循环里拼接字符串,必须用 StringBuilder!
// 直接用 + 每次都新建对象,性能差
StringBuilder sb = new StringBuilder();
for (int i = 0; i < 5; i++) {
sb.append("item").append(i).append(" ");
}
String result = sb.toString();
System.out.println(result); // item0 item1 item2 item3 item4
// StringBuilder 常用方法
StringBuilder b = new StringBuilder("Hello");
b.append(", World"); // 追加
b.insert(5, " Java"); // 在索引处插入
b.delete(5, 10); // 删除 [5, 10)
b.reverse(); // 反转
System.out.println(b); // dlroW ,olleH
// 链式调用:每个方法都返回 this
String chained = new StringBuilder()
.append("A").append("B").append("C")
.toString();
System.out.println(chained); // ABC
}
}String vs StringBuilder vs StringBuffer:
- String:不可变,线程安全,适合不修改的场景。
- StringBuilder:可变,非线程安全,性能最好,单线程首选。
- StringBuffer:可变,线程安全(方法加了 synchronized),性能略差,多线程共享时用。
实际开发 99% 用 StringBuilder,多线程共享字符串的场景极少。
5. 字符串与基本类型互转
这是处理用户输入、解析配置时的核心操作:
public class StringConvert {
public static void main(String[] args) {
// String -> 基本类型:用包装类的 parseXxx
int i = Integer.parseInt("123");
double d = Double.parseDouble("3.14");
boolean b = Boolean.parseBoolean("true");
System.out.println(i + " " + d + " " + b);
// 基本类型 -> String:三种方式
String s1 = String.valueOf(123); // 推荐
String s2 = Integer.toString(123);
String s3 = 123 + ""; // 最简单但可读性差
// ⚠️ parseInt 遇到非数字会抛 NumberFormatException
try {
int n = Integer.parseInt("abc"); // ❌ 抛异常
} catch (NumberFormatException e) {
System.out.println("格式错误: " + e.getMessage());
}
}
}记住两个常用 API:Integer.parseInt(s)(字符串转 int)、String.valueOf(x)(任意类型转字符串)。parseInt 失败抛 NumberFormatException,生产代码要捕获。
6. 字符串格式化
需要把多个变量拼到模板字符串里时,用 String.format 或 System.out.printf:
public class StringFormat {
public static void main(String[] args) {
String name = "小明";
int age = 20;
double score = 95.5;
// String.format:返回格式化字符串
String s1 = String.format("姓名: %s, 年龄: %d", name, age);
System.out.println(s1); // 姓名: 小明, 年龄: 20
// 常用占位符:
// %s 字符串 %d 整数 %f 浮点 %c 字符 %b 布尔
// %n 换行 %% 百分号本身
// 控制宽度和精度
System.out.println(String.format("分数: %.2f", score)); // 95.50
System.out.println(String.format("|%10d|", 42)); // | 42|(右对齐 10 列)
System.out.println(String.format("|%-10d|", 42)); // |42 |(左对齐)
// System.out.printf = print + format 一步到位
System.out.printf("姓名: %s, 分数: %.1f%n", name, score);
}
}7. 字符串编码(进阶)
计算机只认数字,字符要靠编码(UTF-8、GBK)映射成字节。Java 内部用 UTF-16 存字符。读写文件、网络传输时要注意编码:
- 字符串 ↔ 字节转换要显式指定编码,避免乱码:
s.getBytes(StandardCharsets.UTF_8)。 - Java 9+ 对字符串做了压缩(Latin-1 一个字节存),省内存。
- Windows 控制台默认 GBK,打印中文可能乱码,改成 UTF-8 编码就好。
小结
这一章你掌握了 Java 字符串的全部核心:创建、不可变性、常用方法、StringBuilder、类型转换、格式化。新手记住三件事:内容比较用 equals、循环拼接用 StringBuilder、parseInt 要捕获异常。下一篇我们终于进入 Java 的灵魂——类与对象。
← 上一篇 Java 数组
下一篇 Java 类与对象 →