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. 找不到返回 -1,不抛异常。
  2. 支持从指定位置开始查找,适合跳过已匹配内容。
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 为什么不可变?

因为:

  1. String 类被 final 修饰(不能被继承)
  2. 内部的 value 数组也被 final 修饰(不能引用新的数组)
  3. 所有“修改”操作都创建新对象,不会改变原对象内容

不可变不是因为 char[] final,而是因为设计如此。

不可变的好处:

  1. 字符串常量池能够安全使用
  2. 天然线程安全
  3. 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 的字符串既安全、又高效、又易用。

Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐