Java String类:从“看得懂”到“用得稳”的完整指南
Java String类:从“看得懂”到“用得稳”的完整指南
(涵盖关键知识点 + 高频易错点 + 深度理解)
字符串是 Java 世界里使用频率最高的类型,没有之一。
内容如下:
- 为何字符串被设计成不可变?
- 为什么频繁
+=会让你的程序变慢? - 字符串常量池到底怎么工作的?
- String / StringBuilder / StringBuffer 如何选?
- 面试常问的“比较、查找、替换、分割”的正确用法与坑点。
一、String 是什么?为什么它如此重要?
Java 把“字符串”设计成一个真正的类,而不是像 C 语言那样用字符数组拼凑。
原因在于:
- 字符串在程序中无处不在
- 字符操作必须可控
- 字符串要能参与面向对象体系(方法、封装、继承)
Java 中所有用双引号包起来的内容都是 String 对象。
这意味着:
String s = "hello";
并不是基本类型,而是真正的对象。
在 JDK 8 的实现中,String 内部由一个 final char[] value 存储字符数据。
它是“引用类型 + 不可变内容”的经典组合。
二、String 的构造:三种常见写法与差异
1. 字面量(最常用)
String s1 = "hello";
2. new 明确创建对象
String s2 = new String("hello");
new 一定会创建新对象,哪怕内容一样。
3. 由 char 数组构造
char[] arr = {'h','e','l','l','o'};
String s3 = new String(arr);
三、字符串常量池:理解 String 的核心机制
双引号构造的字符串会放入 JVM 的“字符串常量池”。
两个一样的字面量字符串会指向同一个池中对象:
String a = "hello";
String b = "hello";
System.out.println(a == b); // true
但只要出现了 new:
String c = new String("hello");
System.out.println(a == c); // false
易错点:== 比较的是“引用地址是否相同”,不是内容。
要比较内容必须用:
a.equals(c)
四、String 的比较:四种方式、用途完全不同!
1. == 比较引用(是否指向同一对象)
适用于:判断是否同一个对象,而不是判断内容。
2. equals():按字符逐一比较内容
String 重写了 Object 的 equals 方法
比较逻辑包括:
- 是否同一对象
- 类型是否为 String
- 长度是否相同
- 每个字符逐一比较
3. compareTo():字典序比较
返回 int:
-
0:当前字符串更大
- =0:完全相同
- <0:更小
适用于排序。
4. compareToIgnoreCase():忽略大小写的 compareTo
五、查找:indexOf / lastIndexOf 的全面用法
String 提供了一整套查找方法(字符 / 字符串,正向 / 反向,含起始下标)。
关键记住两点:
- 找不到返回 -1,不抛异常。
- 支持从指定位置开始查找,适合跳过已匹配内容。
s.indexOf("abc");
s.indexOf("abc", 10);
s.lastIndexOf('a');
六、转换:数字、大小写、数组、格式化
数字转字符串
String.valueOf(123);
字符串转数字(高频考点)
Integer.parseInt("123");
Double.parseDouble("12.3");
大小写转换
s.toUpperCase();
s.toLowerCase();
字符串 ↔ 数组
char[] arr = s.toCharArray();
String s2 = new String(arr);
格式化
String.format("%d-%d-%d", 2025, 12, 5);
七、替换:replaceAll & replaceFirst
注意:
- 这是“正则替换”!
- 字符串本身不变,会创建新对象。
s.replaceAll("l", "_");
s.replaceFirst("l", "_");
八、分割:split 的两大坑点(面试常问)
s.split(regex);
易错点1:分隔符是正则表达式!
如下必须转义:
"."→"\\.""|"→"\\|""*"→"\\*""+"→"\\+"
分割 IP 地址:
"192.168.1.1".split("\\.");
易错点2:limit 参数用于控制返回数组长度
九、截取:substring(前闭后开)
s.substring(5);
s.substring(0, 5); // [0,5)
区间是“前闭后开”,也就是说:
- 包含起始索引
- 不包含结束索引
十、trim:只去掉左右空白,不动中间内容
s.trim();
空白包含:空格、换行、制表符等。
十一、String 不可变:为什么?带来什么影响?
PDF 针对这个点讲得很清楚,这是 String 的本质。
String 为什么不可变?
因为:
- String 类被
final修饰(不能被继承) - 内部的 value 数组也被
final修饰(不能引用新的数组) - 所有“修改”操作都创建新对象,不会改变原对象内容
不可变不是因为 char[] final,而是因为设计如此。
不可变的好处:
- 字符串常量池能够安全使用
- 天然线程安全
- hashCode 可以缓存,作为 HashMap key 的性能更高
十二、频繁修改字符串:为什么 += 特别慢?
因为每次修改都会:
- 生成新 char[]
- 创建新 String 对象
- 复制旧内容
如果循环里写:
s += i;
你实际上在制造成百上千个临时对象。
因此:
频繁修改字符串必须用 StringBuilder 或 StringBuffer。
十三、StringBuilder 和 StringBuffer:差异与用法
PDF 最后总结的要点非常关键。
1. StringBuilder:线程不安全,但快
适用于:单线程、性能要求高、频繁操作
2. StringBuffer:线程安全,但慢
适用于:多线程需要安全的情况下
核心对比表
| 类型 | 可变 | 线程安全 | 速度 |
|---|---|---|---|
| String | ❌ 不可变 | ✔ 安全 | 慢 |
| StringBuilder | ✔ 可变 | ❌ 不安全 | 快 |
| StringBuffer | ✔ 可变 | ✔ 安全 | 较慢 |
十四、高频易错点总结(非常重要)
构造部分
"hello"和new String("hello")不是一个对象- 字符串常量池只对字面量生效
比较部分
==比较地址- equals 比较内容
- compareTo 用于排序
查找与替换
- indexOf 找不到返回 -1
- replaceAll 是正则,不是普通替换
split
- 分隔符是正则,必须注意转义
substring
- 前闭后开,不包含 endIndex
不可变性
- String 不可修改,所有改变都生成新对象
- 性能敏感代码不要用 += 拼接
StringBuilder / StringBuffer
- 大量拼接 → 用 StringBuilder
- 多线程 → 用 StringBuffer
十五、总结:String 是 Java 中最该“精通”的基础类
理解 String,不只是为了应对面试或写代码,
更是理解 JVM 设计思想的入口。
String 的不可变、常量池、多版本实现优化、配套的 StringBuilder/StringBuffer,这些设计组合起来,使 Java 的字符串既安全、又高效、又易用。
更多推荐



所有评论(0)