Java 核心技术面试精讲
杨晓峰
前 Oracle 首席工程师
125942 人已学习
新⼈⾸单¥59
登录后,你可以任选4讲全文学习
课程目录
已完结/共 44 讲
Java 核心技术面试精讲
15
15
1.0x
00:00/00:00
登录|注册

第5讲 | String、StringBuffer、StringBuilder有什么区别?

带来更小的内存占用、更快的操作速度
Java 9引入了Compact Strings的设计
JDK 8u20后推出G1 GC下的字符串排重
使用intern()方法可以缓存字符串
需要根据实际需求选择合适的字符串拼接方式
StringBuffer和StringBuilder底层都利用可修改的数组
String是Immutable类
通常进行字符串拼接的首选
不具备线程安全性
与StringBuffer功能相似
使用synchronized关键字实现线程安全
线程安全的可修改字符序列
拼接、裁剪字符串会产生新的String对象
Immutable类
隐含使用平台默认编码的实践是否有利于避免乱码
字符串操作中的编码相关问题
3. String自身的演化
2. 字符串缓存
1. 字符串设计和实现考量
StringBuilder
StringBuffer
String
一课一练
知识扩展
典型回答
String、StringBuffer、StringBuilder有什么区别?

该思维导图由 AI 生成,仅供参考

今天我会聊聊日常使用的字符串,别看它似乎很简单,但其实字符串几乎在所有编程语言里都是个特殊的存在,因为不管是数量还是体积,字符串都是大多数应用中的重要组成。
今天我要问你的问题是,理解 Java 的字符串,String、StringBuffer、StringBuilder 有什么区别?

典型回答

String 是 Java 语言非常基础和重要的类,提供了构造和管理字符串的各种基本逻辑。它是典型的 Immutable 类,被声明成为 final class,所有属性也都是 final 的。也由于它的不可变性,类似拼接、裁剪字符串等动作,都会产生新的 String 对象。由于字符串操作的普遍性,所以相关操作的效率往往对应用性能有明显影响。
StringBuffer 是为解决上面提到拼接产生太多中间对象的问题而提供的一个类,我们可以用 append 或者 add 方法,把字符串添加到已有序列的末尾或者指定位置。StringBuffer 本质是一个线程安全的可修改字符序列,它保证了线程安全,也随之带来了额外的性能开销,所以除非有线程安全的需要,不然还是推荐使用它的后继者,也就是 StringBuilder。
StringBuilder 是 Java 1.5 中新增的,在能力上和 StringBuffer 没有本质区别,但是它去掉了线程安全的部分,有效减小了开销,是绝大部分情况下进行字符串拼接的首选。

考点分析

几乎所有的应用开发都离不开操作字符串,理解字符串的设计和实现以及相关工具如拼接类的使用,对写出高质量代码是非常有帮助的。关于这个问题,我前面的回答是一个通常的概要性回答,至少你要知道 String 是 Immutable 的,字符串操作不当可能会产生大量临时字符串,以及线程安全方面的区别。
如果继续深入,面试官可以从各种不同的角度考察,比如可以:
确认放弃笔记?
放弃后所记笔记将不保留。
新功能上线,你的历史笔记已初始化为私密笔记,是否一键批量公开?
批量公开的笔记不会为你同步至部落
公开
同步至部落
取消
完成
0/2000
荧光笔
直线
曲线
笔记
复制
AI
  • 深入了解
  • 翻译
    • 英语
    • 中文简体
    • 中文繁体
    • 法语
    • 德语
    • 日语
    • 韩语
    • 俄语
    • 西班牙语
    • 阿拉伯语
  • 解释
  • 总结

Java中的字符串类包括String、StringBuffer和StringBuilder,它们在设计和使用上有着明显的区别。String是不可变的,任何对它的操作都会生成新的String对象,因此在频繁操作字符串时可能会产生大量临时对象。为了解决这个问题,Java提供了StringBuffer和StringBuilder,它们都是可变的,允许在已有序列的末尾或指定位置添加字符串,但StringBuffer是线程安全的,而StringBuilder则不是。因此,在大多数情况下,推荐使用StringBuilder进行字符串拼接。 理解这些字符串类的设计和实现对于编写高质量的代码至关重要。除了上述基本区别,文章还提到了一些可能的面试考点,如线程安全设计与实现、JVM对象缓存机制、Java代码优化技巧以及String类的演进。这些内容对于深入理解Java字符串类及其在实际开发中的应用都具有重要意义。 文章还介绍了字符串缓存和字符串自身的演化,包括intern()方法、字符串缓存的位置变化以及Java 9中引入的Compact Strings的设计。这些内容展示了Java平台工程师和科学家在字符串优化方面的努力,以及Java字符串类的不断演进和优化。 总之,本文通过简洁明了的方式介绍了Java中String、StringBuffer和StringBuilder的区别,以及相关的技术考点,对于想要快速了解这些内容的读者来说,是一份很有价值的概览。文章内容涵盖了Java字符串类的基本知识和技术特点,对于Java开发者和对字符串优化感兴趣的读者都具有参考价值。

仅可试看部分内容,如需阅读全部内容,请付费购买文章所属专栏
《Java 核心技术面试精讲》
新⼈⾸单¥59
立即购买
登录 后留言

全部留言(143)

  • 最新
  • 精选
  • Bin
    置顶
    jdk1.8中,string是标准的不可变类,但其hash值没有用final修饰,其hash值计算是在第一次调用hashcode方法时计算,但方法没有加锁,变量也没用volatile关键字修饰就无法保证其可见性。当有多个线程调用的时候,hash值可能会被计算多次,虽然结果是一样的,但jdk的作者为什么不将其优化一下呢?

    作者回复: 这些“优化”在通用场景可能变成持续的成本,volatile read是有明显开销的; 如果冲突并不多见,read才是更普遍的,简单的cache是更高效的

    2018-05-16
    12
    87
  • 公号-技术夜未眠
    今日String/StringBuffer/StringBuilder心得: 1 String (1) String的创建机理 由于String在Java世界中使用过于频繁,Java为了避免在一个系统中产生大量的String对象,引入了字符串常量池。其运行机制是:创建一个字符串时,首先检查池中是否有值相同的字符串对象,如果有则不需要创建直接从池中刚查找到的对象引用;如果没有则新建字符串对象,返回对象引用,并且将新创建的对象放入池中。但是,通过new方法创建的String对象是不检查字符串池的,而是直接在堆区或栈区创建一个新的对象,也不会把对象放入池中。上述原则只适用于通过直接量给String对象引用赋值的情况。 举例:String str1 = "123"; //通过直接量赋值方式,放入字符串常量池 String str2 = new String(“123”);//通过new方式赋值方式,不放入字符串常量池 注意:String提供了inter()方法。调用该方法时,如果常量池中包括了一个等于此String对象的字符串(由equals方法确定),则返回池中的字符串。否则,将此String对象添加到池中,并且返回此池中对象的引用。 (2) String的特性 [A] 不可变。是指String对象一旦生成,则不能再对它进行改变。不可变的主要作用在于当一个对象需要被多线程共享,并且访问频繁时,可以省略同步和锁等待的时间,从而大幅度提高系统性能。不可变模式是一个可以提高多线程程序的性能,降低多线程程序复杂度的设计模式。 [B] 针对常量池的优化。当2个String对象拥有相同的值时,他们只引用常量池中的同一个拷贝。当同一个字符串反复出现时,这个技术可以大幅度节省内存空间。 2 StringBuffer/StringBuilder StringBuffer和StringBuilder都实现了AbstractStringBuilder抽象类,拥有几乎一致对外提供的调用接口;其底层在内存中的存储方式与String相同,都是以一个有序的字符序列(char类型的数组)进行存储,不同点是StringBuffer/StringBuilder对象的值是可以改变的,并且值改变以后,对象引用不会发生改变;两者对象在构造过程中,首先按照默认大小申请一个字符数组,由于会不断加入新数据,当超过默认大小后,会创建一个更大的数组,并将原先的数组内容复制过来,再丢弃旧的数组。因此,对于较大对象的扩容会涉及大量的内存复制操作,如果能够预先评估大小,可提升性能。 唯一需要注意的是:StringBuffer是线程安全的,但是StringBuilder是线程不安全的。可参看Java标准类库的源代码,StringBuffer类中方法定义前面都会有synchronize关键字。为此,StringBuffer的性能要远低于StringBuilder。 3 应用场景 [A]在字符串内容不经常发生变化的业务场景优先使用String类。例如:常量声明、少量的字符串拼接操作等。如果有大量的字符串内容拼接,避免使用String与String之间的“+”操作,因为这样会产生大量无用的中间对象,耗费空间且执行效率低下(新建对象、回收对象花费大量时间)。 [B]在频繁进行字符串的运算(如拼接、替换、删除等),并且运行在多线程环境下,建议使用StringBuffer,例如XML解析、HTTP参数解析与封装。 [C]在频繁进行字符串的运算(如拼接、替换、删除等),并且运行在单线程环境下,建议使用StringBuilder,例如SQL语句拼装、JSON封装等。

    作者回复: 很到位

    2018-05-15
    18
    633
  • 愉悦在花香的日子里
    getBytes和String相关的转换时根据业务需要建议指定编码方式,如果不指定则看看JVM参数里有没有指定file.encoding参数,如果JVM没有指定,那使用的默认编码就是运行的操作系统环境的编码了,那这个编码就变得不确定了。常见的编码iso8859-1是单字节编码,UTF-8是变长的编码。

    作者回复: 不错,莫依赖于不确定因素

    2018-05-15
    2
    54
  • Van
    String myStr = "aa" +"bb" + "cc" +"dd";反编译后并不会用到StringBuilder,老师反编译结果中出现StringBuilder是因为输出中拼接了字符串System.out.println("My String:" + myStr);

    作者回复: 嗯,文中的例子有歧义,确实欠考虑

    2018-09-19
    39
  • DoctorDeng
    String s = new String("1"); s.intern(); String s2 = "1"; System.out.println(s == s2); String s3 = new String("1") + new String("1"); s3.intern(); String s4 = "11"; System.out.println(s3 == s4); 这道面试题不错,即考察了 intern() 的用法,也考察了字符串常量池在不同版本 JDK 的实际存储,具体可以看看美团博客:https://tech.meituan.com/in_depth_understanding_string_intern.html,

    作者回复: 思路比结论更有价值

    2018-09-21
    8
    32
  • Jerry银银
    特别喜欢这句话:“仅仅是字符串一个实现,就需要 Java 平台工程师和科学家付出如此大且默默无闻的努力,我们得到的很多便利都是来源于此。” 我想说,同学们,写代码的时候记得感恩哦😄 对于字符串的研究,我觉得能很好的理解计算机的本质和训练计算机思维,提升自己解决问题的能力。 小小的字符串有着诸多巨人的影子

    作者回复: 非常感谢

    2018-05-15
    28
  • 肖一林
    这篇文章写的不错,由浅入深,把来龙去脉写清楚了

    作者回复: 谢谢认可

    2018-05-15
    14
  • 好运来
    老师,可以讲解这一句话的具体含义吗,谢谢! 你可以思考下,原来 char 数组的实现,字符串的最大长度就是数组本身的长度限制,但是替换成 byte 数组,同样数组长度下,存储能力是退化了一倍的!还好这是存在于理论中的极限,还没有发现现实应用受此影响。

    作者回复: 已回复,一个char两个byte,注意下各个类型宽度

    2018-05-15
    9
  • ©®
    String s2=new String("AB"),,如果,常量池中没有AB,那么会不会去常量池创建,望解答

    作者回复: new只是创建新的;另外,有没有想过怎么通过一段程序证明?这样更有助于理解

    2018-05-23
    7
  • So Leung
    经过验证new String时,不会再常量池中创建对象。

    作者回复: 嗯,重要的不是结论,而是如何得到结论

    2018-05-24
    3
    5
收起评论
显示
设置
留言
99+
收藏
沉浸
阅读
分享
手机端
快捷键
回顶部