JavaString知识梳理

📅 2026/7/13 15:47:09
JavaString知识梳理
作者没有四次元口袋的蓝胖日期2026-07-12标签Java, StringJava String知识梳理一、String 的不可变性String 是不可变的immutable一旦创建其值就不能修改。1.1 为什么设计成不可变原因线程安全不可变对象天然线程安全无需同步字符串常量池可以共享相同字符串节省内存hashCode 缓存创建时计算一次 hashCode后续直接使用HashMap 的 key安全性作为参数传递时不会被篡改数据库连接、网络地址等1.2 源码分析publicfinalclassString{privatefinalcharvalue[];// final 修饰引用不可变// Java 9 改为private final byte[] value;publicString(Stringoriginal){this.valueoriginal.value;// 直接复用// ...}}关键final class StringString 类不可继承final char[] value数组引用不可变但数组内容理论上可变Java 通过不提供修改方法保证1.3 不可变的陷阱Stringstrhello;strstr world;// 看起来修改了实际上创建了新对象// 实际过程// 1. 创建 hello// 2. 创建 world// 3. 创建新字符串 hello world// 4. str 引用指向新字符串// 5. 原来的 hello 还在可能被其他引用使用 String 的修改都是创建新对象原对象不变。二、字符串常量池2.1 常量池的作用字符串常量池String Pool是 JVM 堆内存中的一块特殊区域用于存储字符串字面量。优点节省内存相同字符串共享提高效率避免重复创建2.2 创建方式的区别// 方式1字面量常量池Strings1hello;// 方式2new 对象堆内存Strings2newString(hello);// 方式3拼接编译器优化Strings3hello;// 编译期优化为 hello2.3 vs equalsStrings1hello;Strings2hello;Strings3newString(hello);Strings4newString(hello);System.out.println(s1s2);// true同一个常量池对象System.out.println(s1s3);// falses3 在堆中System.out.println(s3s4);// false两个不同的对象System.out.println(s1.equals(s3));// true内容相同规则比较引用内存地址equals()比较内容⚠️String 必须用 equals() 比较内容不能用 。2.4 intern() 方法intern()会将字符串放入常量池如果不存在并返回常量池中的引用。Strings1newString(hello);// 堆中创建Strings2s1.intern();// 将 hello 放入常量池如果不存在Strings3hello;// 从常量池获取System.out.println(s1s2);// falses1 在堆s2 在常量池System.out.println(s2s3);// true都是常量池中的 helloJava 6 vs Java 7Java 6常量池在永久代PermGenintern() 会复制字符串Java 7常量池移到堆中intern() 不会复制只是添加引用三、字符串拼接3.1 拼接方式对比方式类型线程安全性能适用场景语法糖安全低每次创建新对象少量拼接StringBuilder可变不安全高单线程大量拼接StringBuffer可变安全中有同步开销多线程拼接String.concat()方法安全中不常用3.2 编译器优化// 编译期常量拼接优化Strings1hello;// 编译为 hello// 运行期拼接不优化Stringahel;Stringblo;Strings2ab;// 运行期拼接创建新对象// final 修饰优化finalStringahel;finalStringblo;Strings3ab;// 编译期优化为 hello3.3 StringBuilder vs StringBuffer// StringBuilder非线程安全性能高StringBuildersb1newStringBuilder();sb1.append(hello);sb1.append( );sb1.append(world);Stringresult1sb1.toString();// StringBuffer线程安全性能低StringBuffersb2newStringBuffer();sb2.append(hello);sb2.append( );sb2.append(world);Stringresult2sb2.toString();底层实现// StringBuffer 的 append 方法有 synchronizedpublicsynchronizedStringBufferappend(Stringstr){super.append(str);returnthis;}// StringBuilder 的 append 方法无 synchronizedpublicStringBuilderappend(Stringstr){super.append(str);returnthis;}选择建议单线程用 StringBuilder多线程用 StringBuffer少量拼接直接用四、String 常见方法4.1 常用方法速查StringstrHello World;// 长度str.length();// 11// 字符操作str.charAt(0);// Hstr.toCharArray();// [H, e, l, l, o, , W, o, r, l, d]// 截取str.substring(0,5);// Hello// 查找str.indexOf(World);// 6str.lastIndexOf(l);// 9str.contains(World);// truestr.startsWith(Hello);// truestr.endsWith(World);// true// 大小写str.toLowerCase();// hello worldstr.toUpperCase();// HELLO WORLD// 去除空白 hello .trim();// hello// 替换str.replace(World,Java);// Hello Javastr.replaceAll([aeiou],*);// H*ll* W*rld// 分割a,b,c.split(,);// [a, b, c]// 比较str.equals(Hello World);// truestr.equalsIgnoreCase(hello world);// truestr.compareTo(Hello Java);// 正数字典序// 格式化String.format(Name: %s, Age: %d,Tom,20);// Name: Tom, Age: 204.2 性能陷阱// 错误循环中用 拼接每次都创建新对象Stringresult;for(inti0;i1000;i){resulti;// 创建 1000 个 String 对象}// 正确用 StringBuilderStringBuildersbnewStringBuilder();for(inti0;i1000;i){sb.append(i);}Stringresultsb.toString();五、String 与其他类型转换5.1 String ↔ 基本类型// String → intintnum1Integer.parseInt(123);// 123intnum2Integer.valueOf(123);// 123// int → StringStrings1String.valueOf(123);// 123Strings2Integer.toString(123);// 123Strings3123;// 123不推荐// String → doubledoubledDouble.parseDouble(3.14);// String → booleanbooleanbBoolean.parseBoolean(true);5.2 String ↔ 字符数组// String → char[]char[]charshello.toCharArray();// char[] → StringStrings1newString(chars);Strings2String.valueOf(chars);// String → byte[]编码byte[]bytes你好.getBytes(UTF-8);// byte[] → String解码Strings3newString(bytes,UTF-8);六、常见面试题6.1 String、StringBuffer、StringBuilder 的区别对比StringStringBufferStringBuilder可变性不可变可变可变线程安全安全不可变安全synchronized不安全性能低每次创建新对象中高适用少量操作多线程拼接单线程拼接6.2 String 为什么是不可变的安全性作为参数传递时不会被篡改线程安全不可变对象天然线程安全hashCode 缓存创建时计算一次HashMap 的 key 效率高字符串常量池可以共享相同字符串6.3 String s new String(“hello”) 创建了几个对象答案可能创建 1 个或 2 个对象。情况 1常量池中已有 “hello”只创建 1 个对象堆中的 String 对象“hello” 字面量在常量池new 的对象在堆中情况 2常量池中没有 “hello”创建 2 个对象1 个在常量池“hello” 字面量1 个在堆中new String6.4 如何判断 String 是否为空// 错误会 NPEif(str.equals()){}// 推荐if(strnull||str.isEmpty()){}// Java 8if(strnull||str.length()0){}// 兼容老版本// Java 11if(strnull||str.isBlank()){}// 包含空白字符6.5 String.intern() 的作用检查常量池中是否存在该字符串存在则返回常量池中的引用不存在则将字符串放入常量池返回引用应用场景节省内存大量重复字符串字符串比较用 代替 equals七、思维导图速览┌───────────────────────────────────────────────────┐ │ String 高频考点 │ ├───────────────────────────────────────────────────┤ │ │ │ 不可变性 │ │ • final class String │ │ • final char[] value │ │ • 原因安全/线程安全/hashCode缓存/常量池 │ │ │ │ 常量池 │ │ • 字符串字面量存储在常量池 │ │ • new String() 在堆中创建 │ │ • intern() 放入常量池 │ │ • vs equals │ │ │ │ 拼接 │ │ • 语法糖编译器优化 │ │ • StringBuilder单线程性能高 │ │ • StringBuffer多线程性能中 │ │ │ │ 常见方法 │ │ • length/charAt/substring/indexOf/contains │ │ • replace/replaceAll/split/trim │ │ • equals/equalsIgnoreCase/compareTo │ │ │ │ 面试题 │ │ • String vs StringBuffer vs StringBuilder │ │ • 为什么不可变 │ │ • new String(hello) 创建几个对象 │ │ • intern() 的作用 │ │ │ │ 陷阱 │ │ ✗ 用 比较 String │ │ ✗ 循环中用 拼接 │ │ ✗ str.equals() 会 NPE │ │ ✓ 用 equals() 比较内容 │ │ ✓ 用 StringBuilder 拼接 │ │ ✓ str null || str.isEmpty() 判空 │ └───────────────────────────────────────────────────┘八、写在最后String 不可变性是核心要能说出 4 个原因和源码实现字符串常量池是高频考点要理解new String()和字面量的区别StringBuilder vs StringBuffer要知道什么时候用哪个常见陷阱vsequals()、循环中用拼接、空指针判断