【Java】String 详解:底层原理、常量池、不可变性
发布时间:2026/11/2 1:45:37 锦皓数字建站

前言java.lang.String代表字符串Java 中所有双引号括起来的字符串字面量都是 String 对象。核心 String 是不可变字符序列底层在 JDK9 之后由 char [] 改为 byte [] 存储依靠final修饰实现不可变。一、底层源码JDK 8public final class String implements java.io.Serializable, ComparableString, CharSequence { private final char[] value; }final classString 类不能被继承private final char[] value存放字符串内容数组引用不可变数组内容无法外部修改。JDK 9private final byte[] value; private final byte coder;改用byte[] 编码标记coder字符串只有拉丁字符英文 / 数字使用ISO-8859-11 字节存 1 字符节省内存包含中文等使用 UTF-162 字节。目的降低大量英文场景下 String 内存占用。二、String 不可变性重中之重不可变一旦字符串对象创建完成字符串内容不能被修改。注意不是 String 引用不能变是对象本身内容不能修改。示例代码String s abc; s def;并不是把原来abc对象改成def 而是新建一个字符串对象 defs 引用指向新对象旧的 abc 还在内存等待 GC。String str hello; str.concat(world); System.out.println(str); // hello原字符串不变concat、substring、replace这类方法不会修改原字符串只会返回新 String 对象。不可变优点字符串常量池复用多个引用指向同一个常量池对象节省内存哈希值hashCode只需要计算一次适合作为 HashMap key线程安全不可变对象天然线程安全多线程并发访问无竞争。缺点频繁字符串拼接会产生大量中间临时对象造成 GC 压力。解决方案频繁拼接使用StringBuilder/StringBuffer。三、字符串常量池 String Pool字符串常量池存放在 JDK8 之后的堆内存JDK7 从永久代移到堆。作用复用字面量字符串避免重复创建相同字符串对象。两种创建 String 方式1、字面量方式String s1 abc优先去常量池查找常量池存在 abc直接返回常量池对象引用不存在在常量池创建 abc 对象返回引用。2、new 方式String s2 new String(abc)会创建最多 2 个对象先检查常量池有没有 abc没有就在常量池创建new 在堆上再创建一份 String 对象value 数组指向常量池字符数组s2 保存堆中对象的地址。代码对比String s1 abc; String s2 new String(abc); System.out.println(s1 s2); // false // s1指向常量池对象s2指向堆对象地址不一样intern () 方法s.intern()拿到字符串对象去常量池查找。如果常量池已有相同字符串返回常量池引用JDK8常量池没有则把堆中这个字符串引用放入常量池返回该引用。String s new String(a) new String(b); s.intern(); String s2 ab; System.out.println(s s2); // trueJDK8四、字符串拼接1. 字面量编译期优化String s1 a b; // 编译器直接优化成 String s1 ab;编译期常量直接合并放入常量池。2. 变量拼接运行期String a a; String b b; String s3 a b;编译后底层自动创建StringBuilderappend 拼接最后 toString () 生成新 String 对象不会进入常量池。结论带变量的拼接底层是 StringBuilder。循环内用拼接字符串会频繁创建 StringBuilder 和 String性能差。String、StringBuilder、StringBuffer 对比类可变性线程安全性能适用场景String不可变安全低拼接产生大量对象少量字符串常量StringBuilder可变不安全高单线程频繁拼接StringBuffer可变加 synchronized安全较低多线程拼接极少用五、 和 equals比较引用地址基本类型比较值引用类型判断是否是同一个对象。equals()String 重写了该方法比较字符串内容是否相等。String s1 new String(test); String s2 new String(test); System.out.println(s1 s2); // false两个堆对象 System.out.println(s1.equals(s2)); // true内容相同六、常见坑点substringJDK6 存在内存泄漏截取会保留原 char [] 引用JDK7 修复重新拷贝字符数组。空字符串和null是正常 String 对象内容为空null 代表引用没有指向任何对象调用方法会空指针。不要用判断字符串内容业务代码一律用equals最好常量放前面abc.equals(str)防止空指针。大量循环拼接字符串不要用优先 StringBuilder。String 作为 HashMap key因为不可变hashCode 固定非常合适如果可变对象做 key修改后 hash 变化无法 get。七、面试高频问答Q1String 为什么不可变A底层 value 数组用 private final 修饰String 类 final 不可继承。好处常量池复用、hashCode 缓存、线程安全。Q2String snew String (abc) 创建几个对象A最多 2 个。常量池一个堆中 new 一个如果常量池已经存在 abc只会创建堆里 1 个。Q3字符串拼接底层原理A常量字面量相加编译器直接优化合并变量相加底层使用 StringBuilder。Q4intern () 作用JDK7 和 JDK8 有什么变化A将字符串尝试放入常量池。JDK7 及以后常量池放在堆intern 发现常量池没有直接保存堆对象引用不复制对象。Q5StringBuilder 和 StringBuffer 区别AStringBuffer 方法加 synchronized线程安全性能差StringBuilder 非线程安全单线程推荐。结语String 是 Java 基础重中之重核心不可变性、常量池、两种创建对象差异、 拼接底层、intern ()、 与 equals。写代码记住比较字符串内容使用 equals循环拼接使用 StringBuilder。
锦
锦皓数字建站
深耕本土企业品牌数字化升级,专注原创端正雅致商务官网,从视觉设计到稳定运维全程保驾护航。