String 是 Java 中最常用的引用类型之一,用于表示不可变的字符序列,其设计特性(不可变性、常量池)是理解的核心,也是雷区高频出现的根源。

一、核心本质:不可变性(Immutable)

这是 String 最基础、最重要的特性,所有雷区几乎都源于对不可变性的误解

1. 不可变性定义

String 对象创建后,其内部存储的字符序列(private final char[] value,JDK 9+ 改为 byte[])无法被修改。任何看似 “修改” String 的操作(如 substringconcattoUpperCase),本质都是创建新的 String 对象,原对象不变。

2. 不可变性的实现原理(JDK 8)

String 类通过底层数组的 final 修饰和访问权限控制实现不可变:

3. 不可变性的优势线程安全:

不可变对象天然线程安全,无需同步锁(多线程环境下可直接共享)。

  • 常量池复用:相同字符序列的 String 可复用常量池中的对象,减少内存占用(如 String a = "abc" 和 String b = "abc" 指向同一对象)。
  • 哈希值稳定:哈希值在第一次计算后缓存,作为 HashMap 等集合的键时,避免因对象修改导致哈希值变化,进而引发查找异常。

二、创建方式与常量池(高频混淆点)

String 的创建方式有两种,对应 “常量池引用” 和 “堆内存对象”,是面试高频考点,也是新手最易混淆的地方。

1. 两种创建方式对比

创建方式 内存位置 是否复用常量池 本质
String s = "abc" 优先常量池(方法区) 先检查常量池是否有 "abc":有则直接指向,无则在常量池创建并指向。
String s = new String("abc") 堆内存 + 常量池(可选) 1. 先检查常量池:无 "abc" 则创建;2. 无论常量池是否存在,都会在堆中创建新的 String 对象。

2. 代码示例与内存分析

3. 常量池优化:intern() 方法

intern() 是手动将堆中 String 对象 “入池” 的方法,作用是:如果常量池中存在当前字符序列的对象,则返回常量池引用;否则将当前对象的字符序列存入常量池,并返回常量池引用

其他String方法需要用到时,大家参考Java在线文档:https://docs.oracle.com/javase/8/docs/api/index.html

三、常用方法解析(重点与易错点)

String 类提供了大量操作字符序列的方法,以下是高频使用且易出错的方法:

方法 功能描述 易错点提醒
equals(Object obj) 比较字符序列是否相同 1. 与 == 混淆(== 比地址,equals 比内容);2. 避免空指针:"abc".equals(s) 比 s.equals("abc") 安全。
length() 返回字符序列的长度 与数组的 length(属性)混淆:String 是 length() 方法,数组是 arr.length 属性。
substring(int beginIndex, int endIndex) 截取子串(左闭右开:包含 begin,不包含 end) 1. 索引越界异常(beginIndex < 0 或 endIndex > length());2. JDK 6 会创建新数组,JDK 7+ 复用原数组的部分空间(但仍保持不可变性)。
trim() 去除字符串两端的空白字符(空格、制表符等) 1. 仅去除两端,中间空白保留(如 " a b " → "a b");2. 无法去除全角空格(需用 replaceAll("\\s+", ""))。
equalsIgnoreCase(String anotherString) 忽略大小写比较内容 仅比较字符序列,不改变原对象(如 "ABC".equalsIgnoreCase("abc") → true,但原对象仍为 "ABC")。
split(String regex) 按正则表达式分割字符串,返回 String 数组 1. 分割符是正则(如分割 . 需写 split("\\."),因 . 在正则中表示任意字符);2. 末尾空字符串会被丢弃(如 "a,b,".split(",") → ["a", "b"])。

 1.1方法

1. int compareTo(String s) 方法
按照字典序进行比较 与equals不同的是,equals返回的是boolean类型,而compareTo返回的是int类型。具体比较方式: 1. 先按照字典次序大小比较,如果出现不等的字符,直接返回这两个字符的大小差值 2. 如果前k个字符相等(k为两个字符长度最小值),返回值是两个字符串长度差值

  2. int compareToIgnoreCase(String str) 方法:与compareTo方式相同,但是忽略大小写比较

2.2转化

 1. 数值和字符串转化

2.大 小 写 转 换

3. 字 符 串 转 数 组

4. 格 式 化

2.3字符串拆分

代码示例: 实现字符串的拆分处理:

代码示例: 字符串的部分拆分:

拆分是特别常用的操作. 一定要重点掌握. 另外有些特殊字符作为分割符可能无法正确切分, 需要加上转义.

代码示例: 拆分IP地址:

注意事项: 1. 字符"|","*","+"都得加上转义字符,前面加上 "\\" . 2. 而如果是 "\" ,那么就得写成 "\\\\" . 3. 如果一个字符串中有多个分隔符,可以用"|"作为连字符.

代码示例: 多次拆分:

四、高频雷区与容易混淆点(避坑指南)

1. 雷区 1:用 == 比较 String 内容

  • 错误示例

  • 正确做法:使用 equals() 方法比较内容,或用 JDK 1.7+ 新增的 Objects.equals(a, b)(自动处理空指针)。

2. 雷区 2:认为 “修改” String 会改变原对象

  • 错误示例

  • 正确做法:接收 “修改” 方法的返回值,重新赋值:

3. 雷区 3:String、StringBuilder、StringBuffer 混淆

三者均用于字符操作,但核心差异在可变性线程安全,误用会导致性能问题或线程安全问题:

类型 可变性 线程安全 底层实现 适用场景
String 不可变 安全 char[]/byte[] 字符串不频繁修改(如常量定义)
StringBuilder 可变 不安全 char[] 单线程下频繁修改(如拼接字符串)
StringBuffer 可变 安全 char [](方法加 synchronized) 多线程下频繁修改
  • 错误示例:循环中用 String 拼接(每次循环创建新对象,性能极差):

正确做法:用 StringBuilder 拼接(仅一个对象,性能优异):

4. 雷区 4:空字符串("")与 null 混淆

  • 核心差异
    • "":是一个有效对象,长度为 0,有内存地址(存在于常量池或堆)。
    • null:表示没有引用任何对象,不指向任何内存地址,调用方法会抛出 NullPointerException
  • 错误示例

正确做法:先判断非 null,再比较内容:

5. 雷区 5:忽略 String 是引用类型

新手易误以为 String 是基本类型(如 int),但它是引用类型,传递时遵循 “引用传递”(传递的是对象地址,非值本身)。

  • 示例

五、StringBuilder和StringBuffer

由于String的不可更改特性,为了方便字符串的修改,Java中又提供StringBuilder和StringBuffer类。这两个类大 部分功能是相同的。

String和StringBuilder最大的区别在于String的内容无法修改,而StringBuilder的内容可 以修改。频繁修改字符串的情况考虑使用StringBuilder。 注意:String和StringBuilder类不能直接转换。如果要想互相转换,可以采用如下原则: String变为StringBuilder: 利用StringBuilder的构造方法或append()方法 StringBuilder变为String: 调用toString()方法。

六、总结

  1. 核心特性:String 是不可变的 final 类,所有修改操作均创建新对象。
  2. 创建与常量池"abc" 优先复用常量池,new String("abc") 必在堆中创建新对象,intern() 可手动入池。
  3. 方法使用:区分 equals() 与 ==length() 与数组 length,避免 split() 正则陷阱。
  4. 避坑关键
    • 不用 == 比较内容,不用 String 做频繁修改。
    • 区分 "" 与 null,避免空指针。
    • 多线程用 StringBuffer,单线程用 StringBuilder,替代 String 拼接。

更多推荐