【Java】String 源码深度解读:从设计原理到核心方法实战

在 Java 后端开发中,String使用频率最高、面试出镜率最高的基础类之一。绝大多数业务逻辑都离不开字符串拼接、截取、查找、比较等操作,但很多开发者只停留在 API 调用层面,对其底层设计、JDK 优化、源码逻辑并不了解。

本文带你逐行剖析 String 核心源码,搞懂它的不可变性、底层存储优化、常用方法原理,并搭配实战代码,吃透 Java 字符串。


一、String 类的核心声明

先看 String 最顶层的类定义,这是理解它所有特性的基础:

public final class String
    implements java.io.Serializable, Comparable<String>, CharSequence {

关键特性解读

  1. final 修饰String 无法被继承,不存在子类,这是字符串不可变性的基础保障。
  2. 实现 Serializable支持序列化与反序列化,可在网络传输、本地存储中使用。
  3. 实现 Comparable<String>支持自然排序,字符串比较推荐使用 compareTo(),而非 ==== 比较对象地址,equals() 比较内容)。
  4. 实现 CharSequenceStringBufferStringBuilder 同属一个接口,三者为 “近亲”:后两者是可变字符串,适合频繁拼接。

二、底层存储优化:JDK8 char [] → JDK9+ byte []

String 的底层存储结构,在 JDK 版本迭代中有关键内存优化,这是面试与性能调优的高频考点。

1. JDK 8 及之前

private final char value[];
  • char 类型在 JVM 中固定占 2 字节
  • 即使是纯英文、数字(单字节可存储),也会占用 2 字节,内存浪费严重

2. JDK 9 及之后(主流版本)

private final byte[] value;
private final byte coder;
  • 底层改为 byte[] 存储,单字节存储,大幅节省内存
  • 新增 coder 字段区分编码:
    • Latin-1:单字节编码,存储英文、数字
    • UTF-16:双字节编码,存储中文等特殊字符
  • 优势:内存占用降低,GC 频率减少,程序性能提升

三、hashCode ():31 倍哈希法与缓存机制

String 重写了 hashCode(),是它适合作为 HashMap 键的核心原因。

核心源码

private int hash; // 缓存哈希值,默认0

public int hashCode() {
    int h = hash;
    // 未计算过且字符串非空,才重新计算
    if (h == 0 && value.length > 0) {
        byte[] val = value;
        // 31倍哈希法
        for (int i = 0; i < value.length; i++) {
            h = 31 * h + (val[i] & 0xff);
        }
        hash = h; // 缓存结果,避免重复计算
    }
    return h;
}

关键点

  1. 哈希缓存:计算一次后永久缓存,提升效率
  2. 31 倍哈希法
    • 31 是质数,哈希冲突概率低
    • 31 * i = (i << 5) - i,JVM 会自动优化,计算极快
  3. 哈希值分布均匀,让 HashMap 读写效率最优

四、substring ():字符串截取的底层逻辑

substring() 是日常开发最常用的截取方法,源码核心做边界校验 + 新建字符串

简化源码(JDK11)

public String substring(int beginIndex, int endIndex) {
    // 校验索引合法性
    checkBoundsBeginEnd(beginIndex, endIndex, length());
    // 截取整个字符串,直接返回自身
    if (beginIndex == 0 && endIndex == length()) {
        return this;
    }
    // 根据编码创建新字符串
    return isLatin1() ? StringLatin1.newString(value, beginIndex, endIndex - beginIndex)
                      : StringUTF16.newString(value, beginIndex, endIndex - beginIndex);
}

实战代码示例

public class SubstringDemo {
    public static void main(String[] args) {
        String str = "Hello,Java-String-Cloud";

        // 截取 [6, 10) → 左闭右开
        String sub1 = str.substring(6, 10);
        // 从索引7截取到末尾
        String sub2 = str.substring(6);

        System.out.println(sub1); // Java
        System.out.println(sub2); // Java-String-Cloud
    }
}

注意:substring() 一定会返回新的 String 对象(除非截取整个原串)。


五、indexOf ():字符 / 子串查找原理

indexOf() 用于查找字符或子串第一次出现的索引,找不到返回 -1

实战代码示例

public class IndexOfDemo {
    public static void main(String[] args) {
        String str = "java-string-cloud-java";

        // 查找字符'j'第一次出现位置
        int idx1 = str.indexOf('j');
        // 查找子串"string"的起始索引
        int idx2 = str.indexOf("string");
        // 从索引5开始,查找字符'j'
        int idx3 = str.indexOf('j', 5);

        System.out.println(idx1); // 0
        System.out.println(idx2); // 5
        System.out.println(idx3); // 18
    }
}

底层逻辑:遍历字节数组,逐位匹配,找到即返回索引,效率为 O (n)。


六、高频常用方法 & 极简实战

1. 基础判断方法

String str = "JavaBlog";
// 字符串长度
System.out.println(str.length()); // 8
// 判断是否为空串
System.out.println(str.isEmpty()); // false
// 获取指定索引字符
System.out.println(str.charAt(2)); // v

2. 类型转换:valueOf ()

valueOf() 是将任意类型转为字符串的标准方法,底层调用对应包装类 toString()

int num = 1024;
String str1 = String.valueOf(num); // "1024"

double d = 3.14;
String str2 = String.valueOf(d); // "3.14"

3. 字节转换:getBytes ()

String str = "云扬";
// 转UTF-8字节数组
byte[] bytes = str.getBytes(StandardCharsets.UTF_8);
System.out.println(Arrays.toString(str.getBytes(StandardCharsets.UTF_8)));

4. 去空格:trim ()

去除字符串首尾空白字符(空格、制表符、换行符等):

String str = "  Java String  ";
String trimStr = str.trim(); // "Java String"

七、总结(核心考点)

  1. 不可变性final 类 + 底层数组不可修改,线程安全
  2. 存储优化:JDK9+ 用 byte[] + coder 替代 char[],节省内存
  3. 哈希缓存hashCode() 只计算一次,适合做 HashMap
  4. 常用方法:均带索引边界校验,避免 StringIndexOutOfBoundsException
  5. 拼接建议:频繁字符串拼接,优先使用 StringBuilder

Tags:

发表回复

Your email address will not be published. Required fields are marked *.

*
*