面试官问String、StringBuilder、StringBuffer有什么区别附图解性能对比避坑指南摘要String 不可变每次修改创建新对象StringBuilder 可变、线程不安全、性能最高StringBuffer 可变、线程安全synchronized、性能次之。本文用“三种写字工具”比喻 性能实测数据 JVM 底层分析逃逸分析/GC 2026 现代 Java 写法Text Blocks/Records 工具库推荐彻底讲透这道面试必考题。 系列导航上一篇面试官问和equals()有什么区别为什么重写equals必须重写hashCode下一篇预告面试官问final、finally、finalize有什么区别全部85题目录点击查看 面试还原面试官String、StringBuilder、StringBuffer 有什么区别什么场景下用哪个这是 Java 面试中出场率最高的基础题之一。看似简单但面试官可以一路追问到“字符串常量池”、“编译期优化”、“JVM 逃逸分析”、“JDK 9 底层实现变化”。今天用一张图 三种写字工具比喻 性能实测数据 JVM 底层原理让你彻底掌握并应对追问。金句记忆String 不变Builder 快Buffer 安全。单线程拼装用 Builder多线程安全用 Buffer少量操作用 String。 String、StringBuilder、StringBuffer一图看懂 生活比喻三种写字工具想象你需要写一份很长的作业String 钢笔写错了不能擦只能换一张新纸从头写。每修改一次就换一张新纸产生新对象。→ 适合不常变化的字符串如配置信息、常量、方法返回值。StringBuilder 铅笔 橡皮写错了就擦掉重写速度飞快。但旁边的人可能抢你的笔线程不安全。→ 适合单线程下大量拼接如循环内拼接 JSON、SQL、日志。StringBuffer 铅笔 橡皮 保险柜每次用笔都要开锁用完锁上安全但慢方法加 synchronized。→ 适合多线程环境下的字符串操作如缓存构建、多线程日志聚合。 关键对比表维度StringStringBuilderStringBuffer可变性不可变final char[]/byte[]可变可变线程安全安全天然不可变不安全安全synchronized 方法级锁粒度较粗性能最慢每次操作 new 对象最快次快有锁开销适用场景少量操作或常量单线程大量拼接多线程大量拼接存储位置常量池字面量或堆new堆堆继承关系Object 子类AbstractStringBuilder 子类AbstractStringBuilder 子类默认容量—字面量直接存储16无参构造16无参构造扩容机制—旧容量 * 2 2旧容量 * 2 2记忆口诀常变用 Builder多线用 Buffer不变用 String。 面试官追问重点追问1String 为什么设计成不可变四点核心答字符串常量池如果 String 可变一个引用修改会影响其他引用破坏常量池设计。哈希码缓存String 的hashCode只计算一次并缓存可变会导致哈希值变化影响 HashMap 等集合。线程安全天然不可变无需同步。类加载器安全类名通常用字符串表示可变会导致安全漏洞。追问2String s new String(abc)创建了几个对象答如果常量池中没有abc则创建2 个对象常量池一个 堆一个。如果常量池中已有则只创建1 个堆对象。可通过javap -c查看字节码验证。追问3循环内拼接用和StringBuilder.append()哪个好答单行拼接编译器自动优化成StringBuilder两者性能相同。循环内拼接用每次循环会创建新的StringBuilder对象效率低且 GC 压力大。务必用StringBuilder。// ❌ 差每次循环 new StringBuilderStringresult;for(inti0;i10000;i){resulti;// 等价于 result new StringBuilder(result).append(i).toString()}// ✅ 好一个 StringBuilder 复用StringBuildersbnewStringBuilder();for(inti0;i10000;i){sb.append(i);}追问4JVM 的“逃逸分析”能优化字符串拼接吗答逃逸分析JVM 的 JIT 编译器会分析对象是否在方法外可见。标量替换如果对象没有逃逸JIT 会将其拆解为基本类型标量直接在栈上分配避免堆分配和 GC 压力。局限性对于循环内的字符串拼接如result i逃逸分析通常无法优化因为每次迭代都创建新对象并逃逸出当前作用域赋值给循环外变量。结论不要把性能优化完全寄托于 JVM代码层面仍需使用StringBuilder。追问5JDK 9 之后 String 底层有什么变化答JDK 8 及以前private final char[] value;每个字符占 2 字节。JDK 9private final byte[] value;coder字段Latin-1 占 1 字节UTF-16 占 2 字节。目的节省内存。大部分字符串是 Latin-1英文字符用byte[]可节省约 50% 内存。这项优化被称作Compact Strings紧凑字符串。 2026 年的 String 新写法随着 Java 17/21 LTS 的普及以下特性已成为开发标配1. Text BlocksJDK 15 正式当需要拼接大段 SQL、JSON 或 HTML 时Text Blocks 是首选无需StringBuilder的append链// ❌ 旧写法难以阅读Stringjson{\name\:\张三\,\age\:25,\city\:\北京\};// ✅ Text Blocks清晰易读Stringjson { name: 张三, age: 25, city: 北京 } ;适用场景SQL 拼接、JSON 构造、模板生成等。Text Blocks 能极大提升代码可读性。2. RecordsJDK 14 正式JDK 16 完善在 2026 年Records 已是数据载体类的首选。它天然支持 String 的不可变性理念// ❌ 旧写法classUser{privatefinalStringname;privatefinalintage;// 构造器 getter equals hashCode toString}// ✅ Records自动生成一切天然不可变recordUser(Stringname,intage){}Records 的字段是final的与 String 的不可变性一脉相承。 工具库推荐除了原生StringBuilder还有更优雅的选择1. StringJoinerJDK 8当需要带分隔符的拼接时如 CSVStringJoiner比StringBuilder更语义化StringJoinerjoinernewStringJoiner(, ,[,]);joiner.add(甲).add(乙).add(丙);System.out.println(joiner);// [甲, 乙, 丙]优点自动处理首尾分隔符无需手动判断isFirst。2. Guava JoinerGoogle Guava处理集合拼接时更加优雅importcom.google.common.base.Joiner;ListStringlistArrays.asList(甲,乙,丙);StringresultJoiner.on(, ).join(list);// 甲, 乙, 丙// 处理 nullJoiner.on(, ).skipNulls().join(list);优点支持skipNulls、useForNull对 null 友好。 常见坑点坑1字符串拼接 的编译期优化不等于运行时优化// 编译期常量折叠 → 直接变成 hello worldStrings1hello world;// 优化为 hello world// 运行时拼接 → 使用 StringBuilderStrings2hello;Strings3s2 world;// 底层 new StringBuilder().append(s2).append(world).toString()注意只有编译期可知的常量表达式才会被折叠变量拼接不会。坑2StringBuilder 的初始容量设置不当导致频繁扩容StringBuildersbnewStringBuilder();// 默认容量 16for(inti0;i100000;i){sb.append(i);}// 频繁扩容旧容量 * 2 2每次扩容需要复制原数组影响性能正确如果能预估最终长度指定初始容量StringBuildersbnewStringBuilder(100000);// 预分配足够空间坑3多线程环境下误用 StringBuilder// 多线程环境 ❌StringBuildersbnewStringBuilder();// 多个线程同时 sb.append() → 数据错乱、丢失正确使用StringBuffer或显式加锁。但StringBuffer的锁粒度是方法级竞争激烈。高并发下可考虑ThreadLocal或ConcurrentLinkedQueue等无锁方案。坑4认为 StringBuilder 的toString()会复制数组StringBuildersbnewStringBuilder(hello);Stringssb.toString();// JDK 8复制新数组JDK 9共享 value不可变引用JDK 8 及以前toString()会new String(value, 0, count)复制数组保护性复制。JDK 9Compact StringsString构造器接收byte[]时会先检查coder并可能共享数组引用进一步优化性能。 可运行验证代码importjava.util.StringJoiner;importjava.util.Arrays;publicclassStringVsBuilderVsBuffer{publicstaticvoidmain(String[]args){// 1. 性能对比intiterations100000;// String 循环拼接longstart1System.currentTimeMillis();Strings;for(inti0;iiterations;i){si;}longend1System.currentTimeMillis();System.out.println(String 拼接耗时: (end1-start1)ms);// StringBuilderlongstart2System.currentTimeMillis();StringBuildersbnewStringBuilder(iterations*5);for(inti0;iiterations;i){sb.append(i);}longend2System.currentTimeMillis();System.out.println(StringBuilder 耗时: (end2-start2)ms);// StringBufferlongstart3System.currentTimeMillis();StringBufferbuffernewStringBuffer(iterations*5);for(inti0;iiterations;i){buffer.append(i);}longend3System.currentTimeMillis();System.out.println(StringBuffer 耗时: (end3-start3)ms);// 2. 验证不可变性Stringstrhello;Stringstr2str world;System.out.println(str 是否被修改? str);// 还是 hello// 3. 编译期常量折叠验证Stringahello world;Stringbhello world;System.out.println(常量折叠: (ab));// true// 4. StringJoiner 示例StringJoinerjoinernewStringJoiner(, ,[,]);joiner.add(甲).add(乙).add(丙);System.out.println(StringJoiner: joiner);// [甲, 乙, 丙]}}典型输出100000 次String 拼接耗时: 15423ms StringBuilder 耗时: 7ms StringBuffer 耗时: 9ms str 是否被修改? hello 常量折叠: true StringJoiner: [甲, 乙, 丙]❓ 评论区挑战问题下面代码共创建了几个对象不考虑常量池已有的情况Stringsabc;A. 1 个B. 2 个C. 3 个D. 5 个面试官问和equals()有什么区别为什么重写equals必须重写hashCode 评论区挑战问题下面代码的输出是什么为什么Strings1newString(java);Strings2newString(java);System.out.println(s1s2);System.out.println(s1.equals(s2));A. true / falseB. false / trueC. false / falseD. true / true✅ 答案公布正确答案B. false / true解析s1 s2两个对象都是new创建的在堆中不同地址 → false。s1.equals(s2)String 重写了equals()比较字符序列 → true。 系列导航上一篇面试官问和equals()有什么区别为什么重写equals必须重写hashCode下一篇预告面试官问final、finally、finalize有什么区别全部85题目录点击查看你在实际开发中遇到过因为字符串拼接性能问题导致的线上事故吗或者见过哪些“优雅”的字符串拼接写法欢迎评论区分享你的故事。
企业数字化 ERP 产品动态
相关推荐
电脑监控软件哪个好用?精选6款企业级电脑监控软件,最新排行榜 很多企业在管理员工电脑的时候,都会遇到一个问题:到底选哪款电脑监控软件才真正好用?有的只看得到屏幕,有的只能记录上网行为,还有的功能零散,用起来反而更麻烦。其实现在企业更看重的是一套“能管、能查、… · 2026/9/22 8:29:22
终极Windows Defender修复指南:no-defender工具的决策流程图解法 终极Windows Defender修复指南:no-defender工具的决策流程图解法 【免费下载链接】no-defender A slightly more fun way to disable windows defender firewall. (through the WSC api) 项目地址: https://gitcode.com/GitHub_Trending/no/no-defender
当W… · 2026/9/22 10:09:38
GitHub520:终极GitHub加速解决方案,5分钟解决访问慢和图裂问题 GitHub520:终极GitHub加速解决方案,5分钟解决访问慢和图裂问题 【免费下载链接】GitHub520 :kissing_heart: 让你“爱”上 GitHub,解决访问时图裂、加载慢的问题。(无需安装) 项目地址: https://gitcode.com/GitHub_… · 2026/9/15 22:10:24
策略模式实战:消除if-else,实现可扩展的折扣系统 1. 策略模式到底解决了什么问题第一次接触策略模式是在做一个电商促销模块的时候。当时产品提了一个需求:商品要支持多种折扣方式,包括满减、打折、会员价、限时秒杀价,而且后续还会不断增加新的促销类型。我一开始的做法很简单,写… · 2026/9/23 13:20:34
鱼香鸡蛋源码解析:从语法到项目的3个关键步骤 鱼香鸡蛋源码解析:从语法到项目的3个关键步骤 学会语法却不知怎么搭项目,这是多数开发者卡在初级阶段的死结。你背熟了 for 循环和 if 判断,打开 IDE 却对着空白文件发呆。别急, 源码解析… · 2026/9/23 13:20:34
Android加密从Blowfish迁移到AES-GCM:安全选型与实战改造指南 接手过一个老项目,里面对用户手机号做加密存储用的就是Blowfish,当时第一反应是“这玩意儿还活着呢?”查了一圈资料发现,Blowfish确实是加密算法界的“老前辈”,1993年由Bruce Schneier设计的对称分组密码,… · 2026/9/23 13:20:34
从ff.c到invalid signature:嵌入式文件系统与固件签名全解析 前两天在 Gitee 上翻一个嵌入式开源项目,仓库地址是 wuming/fatfs,点进去看的是 ff.c 这个文件,浏览器标题栏里挂着一串 signature8078a4ab63c88f9c690526bc05ffbacc。这串字符第一次看像是随机乱码,实际上它是 Git 体系里的提交哈… · 2026/9/23 13:20:33
Python 2.7+Scrapy 1.4电商爬虫实战:京东淘宝天猫反爬适配指南 简介:这是一套面向数据采集工程师、电商研究者及Python初学者的实战型爬虫工具包,旨在解决京东、淘宝、天猫三大平台商品信息高效抓取难题,尤其适用于市场分析、竞品监控与价格趋势研究等场景。资源共20个文件,含12个核心Python脚… · 2026/9/23 13:20:27
L阵列结合Matrix Pencil的二维DOA估计:原理与MATLAB实现 简介:面向无线通信、雷达探测、声学成像等领域中的二维到达角(DOA)估计学习者,这份MATLAB实现包聚焦基于增广矩阵束的L型阵列DOA估计方法。代码将相互垂直的两个子阵列接收数据融合为增广矩阵,通过矩阵束处理联合求解信… · 2026/9/23 13:20:27
3招搞定手机怎么下载微信面试难题实战项目解析 3招搞定手机怎么下载微信面试难题实战项目解析 面试被问“手机怎么下载微信”背后的原理,90%的人答不上来。别笑,这看似弱智的问题,实则是考察你对移动应用分发机制、安全校验及网络协议理解的试金石。我带过不少校招新人,他们背了八股文,却连一个A… · 2026/9/23 0:00:03
你有新短消息请注意查收:3个新手避坑指南搞定消息系统选型 你有新短消息请注意查收:3个新手避坑指南搞定消息系统选型 面试被问“高并发下如何保证消息不丢失”,你张口就是“用Redis”,结果面试官追问“如果Redis宕机了怎么办”,你瞬间卡壳。这种场景太常见了,很多新手在背八股文时,只记住了技术名词… · 2026/9/23 0:00:29