一文讲透:String 为什么不可变?StringBuffer 与 StringBuilder 的区别

发布时间:2026/10/5 16:57:59

一文讲透:String 为什么不可变?StringBuffer 与 StringBuilder 的区别 一文讲透String 为什么不可变StringBuffer 与 StringBuilder 的区别图解字符串是开发和校招笔试中的常客字符串转整形数字、字符串相加……面试中也频繁被问到String、StringBuffer 和 StringBuilder 之间的区别。本文用图解 源码的方式讲清楚三个问题String 为什么不可修改哪两种类型是可以修改的这两种可修改的类型有什么区别目录一、String 为什么不可变1.1 从源码看final 类 private final value 数组1.2 「不可变」的真正含义所有修改都是创建新对象1.3 误区澄清不是因为 final 所以内容不能改1.4 为什么要设计成不可变不可变对象的好处二、想修改字符串怎么办两种可修改的类型2.1 直接用 String 修改效率非常低2.2 StringBuilder内容可修改的字符串2.3 String 与 StringBuilder 的相互转换三、StringBuffer 与 StringBuilder 的区别面试题四、总结一、String 为什么不可变1.1 从源码看final 类 private final value 数组String 是一种不可变对象字符串中的内容是不可改变的。这是因为String 类在设计时就是不可改变的。来看 JDK 1.8 中 String 类的部分实现——String 类中的字符实际保存在内部维护的value 字符数组中publicfinalclassStringimplementsjava.io.Serializable,ComparableString,CharSequence{/** * The value is used for character storage. */privatefinalchar[]value;// 字符真正存放的地方// ...提供大量读取 value 的方法但没有任何修改 value 内容的方法}从源码中可以读出三层设计意图String 类被 final 修饰表明该类不能被继承防止子类重写行为破坏不可变性value 被 private final 修饰private外部访问不到valuefinalvalue 自身的值不能改变即不能修改引用的指向但是指向中的内容是可以修改的没有任何修改入口String 类内部没有提供任何修改 value 内容的方法所有 APIreplace、substring、concat……都只会返回新对象。1.2 「不可变」的真正含义所有修改都是创建新对象所有涉及到可能修改字符串内容的操作都是创建一个新对象、改变的是新对象而原对象丝毫不动。以最熟悉的s world为例publicstaticvoidmain(String[]args){Stringshello;s world;// 并没有在原字符串上追加而是产生了新对象System.out.println(s);// 输出hello world}所以像下面这样的替换操作Stringstrhelloworld;System.out.println(str.replaceAll(l,_));// he__owor_dSystem.out.println(str.replaceFirst(l,_));// he_loworldSystem.out.println(str);// helloworld原字符串纹丝不动注意事项由于字符串是不可变对象替换不会修改当前字符串而是产生一个新的字符串。必须用变量接收返回值否则修改毫无意义。1.3 误区澄清不是因为 final 所以内容不能改网上有人说字符串不可变是因为其内部保存字符的数组被 final 修饰了因此不能改变——这种说法是错误的。final修饰引用类型时仅表明该引用变量不能引用其他对象指向不能变但其引用对象中的内容依然可以修改publicstaticvoidmain(String[]args){finalint[]array{1,2,3,4,5};array[0]100;// 编译通过内容可以修改System.out.println(Arrays.toString(array));// [100, 2, 3, 4, 5]// array new int[]{4, 5, 6}; // 编译报错无法为最终变量 array 分配值}一句话总结final 修饰类表明该类不想被继承final 修饰引用类型表明该引用不能引用其他对象但引用对象中的内容是可以修改的。字符串真正不可变是因为类不能被继承 value 拿不到也换不掉 类中没有任何修改内容的方法所有 API 都返回新对象。1.4 为什么要设计成不可变不可变对象的好处既然修改这么麻烦为什么 String 要设计成不可变的呢方便实现字符串对象池字符串常量池如果 String 可变对象池就需要考虑写时拷贝的问题了——共享同一个对象时一个引用的修改会波及所有使用者不可变对象是线程安全的多线程可以放心共享无需加锁不可变更方便缓存 hash code作为 key 时可以更高效地保存到 HashMap 中。二、想修改字符串怎么办两种可修改的类型2.1 直接用 String 修改效率非常低注意尽量避免直接对 String 类型对象进行修改因为 String 类是不能修改的所有的修改都会创建新对象效率非常低下。publicstaticvoidmain(String[]args){longstartSystem.currentTimeMillis();Strings;for(inti0;i10000;i){si;// 每次循环都创建新对象}longendSystem.currentTimeMillis();System.out.println(end-start);// 耗时很长startSystem.currentTimeMillis();StringBuffersbfnewStringBuffer();for(inti0;i10000;i){sbf.append(i);// 原地修改无新对象}endSystem.currentTimeMillis();System.out.println(end-start);startSystem.currentTimeMillis();StringBuildersbdnewStringBuilder();for(inti0;i10000;i){sbd.append(i);// 原地修改无新对象}endSystem.currentTimeMillis();System.out.println(end-start);}可以看到在对 String 类进行修改时效率是非常慢的循环拼接 10000 次会产生约 10000 个临时对象。因此尽量避免对 String 的直接修改如果要修改建议尽量使用 StringBuffer 或者 StringBuilder。2.2 StringBuilder内容可修改的字符串由于 String 的不可更改特性为了方便字符串的修改Java 中又提供了StringBuilder 和 StringBuffer两个类——这就是内容可以修改的两种类型。这两个类大部分功能是相同的以 StringBuilder 为例注意不能直接赋值字符串只能通过构造方法传入publicstaticvoidmain(String[]args){StringBuildersb1newStringBuilder(hello);// 不能写 sb1 helloStringBuildersb2sb1;// 追加即尾插 -- 字符、字符串、整形数字sb1.append( );// hellosb1.append(world);// hello worldsb1.append(123);// hello world123System.out.println(sb1);// hello world123System.out.println(sb1sb2);// trueappend 是原地修改对象没变System.out.println(sb1.charAt(0));// 获取 0 号位上的字符 hSystem.out.println(sb1.length());// 获取字符串的有效长度 14System.out.println(sb1.capacity());// 获取底层数组的总大小sb1.setCharAt(0,H);// 设置任意位置的字符 - Hello world123sb1.insert(0,Hello world!!!);// 指定位置插入 - Hello world!!!Hello world123System.out.println(sb1);System.out.println(sb1.indexOf(Hello));// 获取 Hello 第一次出现的位置System.out.println(sb1.lastIndexOf(hello));// 获取 hello 最后一次出现的位置sb1.deleteCharAt(0);// 删除首字符sb1.delete(0,5);// 删除 [0, 5) 范围内的字符Stringstrsb1.substring(0,5);// 截取 [0, 5) 区间中的字符以 String 方式返回System.out.println(str);sb1.reverse();// 字符串逆转strsb1.toString();// 将 StringBuilder 以 String 方式返回System.out.println(str);}StringBuilder 常用方法一览作用 返回值方法作用返回值StringBuilder append(String str)在尾部追加相当于 String 的可追加 boolean、char、char[]、double、float、int、long、Object、String 等返回自身支持链式调用StringBuilder insert(int offset, String str)在 offset 位置插入八种基本类型 String Object 数据返回自身StringBuilder deleteCharAt(int index)删除 index 位置的字符返回自身StringBuilder delete(int start, int end)删除[start, end)区间内的字符返回自身StringBuilder replace(int start, int end, String str)将[start, end)位置的字符替换为 str返回自身StringBuilder reverse()反转字符串返回自身char charAt(int index)获取 index 位置的字符返回该字符charvoid setCharAt(int index, char ch)将 index 位置的字符设置为 ch无返回值int indexOf(String str)返回 str 第一次出现的位置返回下标int indexOf(String str, int fromIndex)从 fromIndex 位置开始查找 str 第一次出现的位置返回下标int lastIndexOf(String str)返回 str 最后一次出现的位置返回下标int lastIndexOf(String str, int fromIndex)从 fromIndex 开始找 str 最后一次出现的位置返回下标String substring(int start)从 start 开始一直到末尾的字符以String方式返回不改变自身String substring(int start, int end)将[start, end)范围内的字符以String方式返回不改变自身int length()获取字符串的有效长度返回长度int capacity()获取底层保存字符串空间总的大小返回容量void ensureCapacity(int minimumCapacity)扩容无返回值String toString()将所有字符按照 String 的方式返回返回String对象从上述例子可以看出String 和 StringBuilder 最大的区别在于 String 的内容无法修改而 StringBuilder 的内容可以修改。频繁修改字符串的情况考虑使用 StringBuilder。2.3 String 与 StringBuilder 的相互转换注意String 和 StringBuilder 类不能直接转换。如果要想互相转换可以采用如下原则String 变为 StringBuilder利用 StringBuilder 的构造方法或append()方法StringBuilder 变为 String调用toString()方法。Stringshello;// String - StringBuilderStringBuildersbnewStringBuilder(s);// 方式一构造方法sb.append( world);// 方式二append// StringBuilder - StringStringretsb.toString();三、StringBuffer 与 StringBuilder 的区别面试题String、StringBuffer、StringBuilder 的区别——这是一道高频面试题核心区别如下String 的内容不可修改StringBuffer 与 StringBuilder 的内容可以修改StringBuffer 与 StringBuilder 大部分功能是相似的StringBuffer 采用同步处理属于线程安全操作而 StringBuilder 未采用同步处理属于线程不安全操作。对比维度StringStringBufferStringBuilder内容可变性不可变修改必产生新对象可修改可修改线程安全安全不可变天然安全安全方法加了同步处理不安全未做同步处理性能频繁修改时极低较高同步有少许开销最高使用场景内容基本不变、作为常量共享多线程环境下频繁修改单线程环境下频繁修改绝大多数场景首选四、总结String 不可变的原因源码级String 类被final修饰不能被继承防止子类破坏不可变性value 数组被private final修饰外部访问不到、引用也不能换类中没有任何修改 value 内容的方法所有涉及修改的操作replace、substring、……都是创建新对象注意误区不是因为 final 导致内容不可变——final 修饰引用类型只是指向不能变内容本可以改不可变的好处方便实现字符串常量池、线程安全、方便缓存 hash code两种可修改的类型StringBuffer与StringBuilder内容可直接原地修改频繁修改字符串时用它们替代 Stringappend返回自身、支持链式调用toString()转回 String两者的区别StringBuffer 同步处理、线程安全StringBuilder 未同步、线程不安全但效率最高——单线程首选 StringBuilder多线程共享时选 StringBufferString 与 StringBuilder不能直接相互赋值/转换只能通过构造方法 / appendString → StringBuilder和toStringStringBuilder → String。如果这篇文章对你有帮助欢迎点赞 收藏 ⭐ 关注后续会继续更新 Java 系列文章
延伸阅读

更多相关文章

2026/10/5 16:57:59

『ISOBUS 入门』第 03 节 OSI 模型与 ISO 11783 分册映射

1. 分层与分册的对应 这张图把七层参考模型与分册编号对齐,可以读成四段: OSI 层 对应分册 说明 应用层 ISO 11783-7/8/9/10/12/13/14 农具报文、动力总成、拖拉机 ECU、任务控制器、诊断、文件服务器、顺序控制 表示层 ISO 11783-6/7/11 虚拟终端的对象与数据元呈现规则 会话…

2026/10/5 16:57:59

『ISOBUS 入门』第 14 节 TECU 与 TIM:拖拉机侧的网关与双向协同

拖拉机内部使用的网络协议与农具侧并不相同,农具要读车速、转速、悬挂状态,只能通过一个翻译层。TECU(Tractor ECU,ISO 11783-9)就是这个翻译层:它把拖拉机侧的数据按标准报文发布到 ISOBUS 上,也在需要时把农具侧的请求转回拖拉机内部。 1. 三个等级是「最小消息集」,…

2026/10/5 17:58:01

从零搭建AI工程体系:分层架构、评测与可观测性实战

1. 从零搭建AI工程能力:为什么我劝你别一上来就调包这两年AI应用开发的门槛肉眼可见地降低了,一个刚入门的开发者,装好环境、拿到API Key,半天时间就能跑出一个能对话的Demo。但我带过不少新人,也看过很多团队的项目&a…

2026/10/5 17:58:01

基于Node.js与React构建能思考与行动的AI智能体实战

1. 项目缘起:为什么我要用 paperclip 把 AI 智能体接到真实工作流里第一次看到paperclip这个词,很多人脑子里蹦出来的可能是办公桌上那枚弯弯的金属夹子。但在 Node.js 和 React 的圈子里,它指的是一类很具体的东西:一个把 AI age…

2026/10/5 17:58:01

HSV颜色空间实现红章精准提取与透明PNG生成

1. 这不是“抠图”,是工业级颜色定位与Alpha通道重建 你手头有一张扫描件,白纸黑字,但角落盖着一个鲜红的公章——现在要把它单独抠出来,背景必须是完全透明的PNG,后续要叠在PDF水印层、嵌入电子合同签名区、或者喂给O…

2026/10/5 17:53:01

yolov5单目测距系统毕设拆解:从A4标定到误差量化

简介:这份资源是经导师指导并获98分认可的毕业设计项目,围绕YOLOv5实现单目测距系统,面向计算机相关专业正在做毕设、课程设计或期末大作业的学生,也适合需要项目实战练习的学习者。项目已通过严格调试,可直接作为毕设…

2026/10/5 17:53:01

AMESim柱塞泵建模全流程:从HCD库到Simulink联合仿真

做液压仿真这行,绕不开柱塞泵,更绕不开AMESim。我早些年刚接触AMESim的时候,光是想把一台斜盘式轴向柱塞泵的模型搭出来,就折腾了将近两周。看论文觉得原理都懂,打开软件却不知道从哪个库拖元件、参数填多少、为什么一…

2026/10/5 6:32:56

Jev+Agent接管浏览器:browser-use实战与jev-ultrafast性能优化

1. 从“Jev”说起:为什么我要把Agent接进浏览器“Jev”这个词最近在圈子里出现的频率越来越高,很多人第一次听到会以为是某个新模型的名字,其实它更像是一种思路——把Jev模型的能力当作底座,通过Agent的方式去接管浏览器&#xf…

2026/10/4 0:01:02

多智能体集群实战:DeepAgents编排、MCP与A2A协议及Skills体系

1. 从"单兵作战"到"集群协同":多智能体编排到底在解决什么问题如果你最近在折腾 Agent 相关的东西,大概率会有一种感觉:单个 Agent 能做的事情,其实很快就摸到天花板了。你给它一个提示词,挂几个工…

2026/10/5 17:38:27

无源低通滤波器设计实战:从RC到LC,手把手教你避开那些坑

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

还想了解更多?直接咨询顾问

免费诊断 + 免费方案 + 透明报价。

全国咨询热线400-8866-253
免费获取方案
☎咨询二维码 ☎ ↑