Loading [MathJax]/jax/output/CommonHTML/config.js
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >面试题系列第8篇:谈谈String、StringBuffer、StringBuilder的区别?

面试题系列第8篇:谈谈String、StringBuffer、StringBuilder的区别?

作者头像
程序新视界
发布于 2020-09-17 09:37:54
发布于 2020-09-17 09:37:54
97000
代码可运行
举报
文章被收录于专栏:丑胖侠丑胖侠
运行总次数:0
代码可运行

Java面试题系列》:一个长知识又很有意思的专栏。深入挖掘、分析源码、汇总原理、图文结合,打造公众号系列文章,面试与否均可提升Level。欢迎持续关注【程序新视界】。第8篇。

关于字符串的面试题除了内存分布、equals比较,最常见的就是与StringBuffer和StringBuilder之间的区别了。

如果你回答:String类是不可变的,StringBuffer和StringBuilder是可变类,StringBuffer是线程安全的,StringBuilder则不是线程安全的。

就上面的总结而言,好像知道的有点少。本篇文章就带领大家全面的了解一下它们三个的区别与底层实现。

String字符串的拼接

关于String字符串前面多篇文章已经详细描述过,它的不可变性也是因为每当通过“+”操作时,都会在内存中生成新的字符串而导致的。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
String a = "hello ";
String b = "world!";
String ab = a + b;

针对上述代码,内存分布图如下:

其中a和b初始化时位于字符串常量池,ab拼接后的对象位于堆中。可以很直观的看出,经过拼接新生成了String对象。如果拼接多次,那么会生成多个中间对象。

上面的结论在Java8之前是成立的,在Java8时JDK对“+”号拼接进行了优化,上面所写的拼接方式会被优化为基于StringBuilder的append方法进行处理。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
stack=2, locals=4, args_size=1
     0: ldc           #2                  // String hello
     2: astore_1
     3: ldc           #3                  // String world!
     5: astore_2
     6: new           #4                  // class java/lang/StringBuilder
     9: dup
    10: invokespecial #5                  // Method java/lang/StringBuilder."<init>":()V
    13: aload_1
    14: invokevirtual #6                  // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
    17: aload_2
    18: invokevirtual #6                  // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
    21: invokevirtual #7                  // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
    24: astore_3
    25: return

上面是通过javap -verbose命令反编译字节码的结果,很显然可以看到StringBuilder的创建和append方法的调用。

此时,如果再笼统的回答:通过加号拼接字符串会创建多个String对象,因此性能比StringBuilder差,就是错误的了。因为本质上加号拼接的效果最终经过编译器处理之后和StringBuilder是一致的。

如果你在代码中使用如下写法:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
StringBuilder sb = new StringBuilder("hello ");
sb.append("world!");
System.out.println(sb.toString());

编译器的插件甚至建议你使用String来代替。

StringBuffer与StringBuilder的对比

StringBuffer和StringBuilder实现的核心代码基本一致,很多代码都是公用的。这两个类均继承自抽象类AbstractStringBuilder。

我们来从构造方法到append方法来逐一看一下它们的区别。先看StringBuilder的构造方法:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public StringBuilder(String str) {
    super(str.length() + 16);
    append(str);
}

其中super方法便是调用的AbstractStringBuilder的构造方法。对应StringBuffer的构造方法中实现也是如此:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public StringBuffer(String str) {
    super(str.length() + 16);
    append(str);
}

从构造方法来说,StringBuffer和StringBuilder是一样的。下面再看看append方法,StringBuilder实现如下:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
@Override
public StringBuilder append(String str) {
    super.append(str);
    return this;
}

StringBuffer对应的方法如下:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
@Override
public synchronized StringBuffer append(String str) {
    toStringCache = null;
    super.append(str);
    return this;
}

很显然,在StringBuffer的append方法实现上除了内部将toStringCache变量赋值为null,唯一的不同就是在方法上使用synchronized进行了同步处理。

toStringCache是用来缓存最后一次调用toString方法时生成的字符串,当StringBuffer内容变动时,改值也会变动。

通过上面的append方法的对比,我们可以很轻易的发现StringBuffer是线程安全的,StringBuilder是非线程安全的。当然,使用synchronized进行同步处理,性能便会降低很多。

StringBuffer与StringBuilder的底层实现

StringBuffer与StringBuilder都调用了父类的构造方法:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
AbstractStringBuilder(int capacity) {
    value = new char[capacity];
}

通过该构造方法我们可以看到它们用来处理字符串信息的关键属性为value。在初始化时先初始化一个长度为传入字符串长度+16的char[]数组,也就是value值,用来存储实际的字符串。

在调用父类构造方法之后便是调用各自的append方法(见前面的代码),而其中的核心处理又的调用父类的append方法:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public AbstractStringBuilder append(String str) {
    if (str == null)
        return appendNull();
    int len = str.length();
    ensureCapacityInternal(count + len);
    str.getChars(0, len, value, count);
    count += len;
    return this;
}

上述代码中其中str.getChars方法用来对传入的str字符串进行拼接,在原有的value数组后面进行填充。而count用来记录当前value数字中已经使用的长度。

那么,当没有使用synchronized进行同步操作时,线程不安全发生在哪里?上面代码中count+=len并不是原子操作。比如当前count为5,两个线程同时执行到++操作,拿到的值都为5,执行完加操作之后赋值给count,两个线程赋值都为6,而不是7。此时便出现了线程不安全的问题。

为什么String要设计成不可变

在Java中将String设计成不可变的是综合考虑到各种因素的结果,有如下原因:

1、字符串常量池的需要,如果字符串可变,改变一个对象会影响到另外一个独立的对象。不变这也是字符串常量池存在的前提条件。

2、Java中String对象的哈希码被频繁地使用,比如在HashMap等容器中。字符串不变保证了hash码的唯一性,可以方向缓存并使用。

3、安全性,确保String在当做参数传递时保持不变,避免安全隐患。比如在数据库用户名、密码、访问路径等传输过程中的保持不变,防止改变字符串指向对象的值被改变。

4、由于字符串变量不可变,在多线程中可以被共享使用。

小结

单纯的死记硬背面试题我们都会,但要在记忆面试题的过程中了解更多底层实现原理,不仅仅有助于理解“为什么”,同时还能学到更多相关的知识和原理。

在本文中简化了StringBuilder和StringBuffer内部数据的copy、数组扩容等步骤的讲解,感兴趣的朋友可以继续对照源码进行深入研究。

原文链接:《面试题系列第8篇:谈谈String、StringBuffer、StringBuilder的区别?


本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2020/09/15 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
StringBuffer 和 StringBuilder 的 3 个区别!
这么简单的一道题,栈长在最近的面试过程中,却经常遇到很多求职者说反,搞不清使用场景的情况。
Java技术栈
2019/07/08
6270
StringBuffer 和 StringBuilder 的 3 个区别!
通过源码分析 String、StringBuffer 和 StringBuilder
0x00 简介 又翻出来了在15年整理的笔记了。感觉当初还是挺较真的。 自己对String的理解总是存在着不同程度的误差,经常处于一知半解的状态,而且对其内部的原理也不是特别清楚,碰巧又和同学聊起这个知识点,秉承爱折腾的原则,在论文答辩之际详细整理一下。 0x01 说明 最初听说的String、StringBuffer和StringBuilder三者之间的区别主要是下面这个版本(略作总结): String:字符串常量,字符串长度不可变。Java中String是immutable(不可变)的。用于存放字符的数
木东居士
2018/05/25
5610
这么答才叫有效面试!请谈谈String、StringBuffer和StringBuilder的区别
可能是因为毕业季,最近有非常多的应届生来咨询我面试相关的问题。这不,今天有一位应届生找到我,说面试被问到这样一道面试题:请谈谈String、StringBuffer和StringBuilder的区别。其实呢,这是一道老生常谈的题,光背八股文还是非常简单的。
Tom弹架构
2023/08/10
1760
这么答才叫有效面试!请谈谈String、StringBuffer和StringBuilder的区别
【Java面试】毕业季高频面试题String,StringBuffer好和StringBuilder的区别
可能是因为毕业季,最近有非常多的应届生来咨询我面试相关的问题。这不,今天有一位应届生找到我,说面试被问到这样一道面试题:请谈谈String、StringBuffer和StringBuilder的区别。其实呢,这是一道老生常谈的题,光背八股文还是非常简单的。
Tom弹架构
2022/12/19
2320
【Java面试】毕业季高频面试题String,StringBuffer好和StringBuilder的区别
String及StringTable(四):StringBuffer与StringBuilder对比
分析完StringBuilder,然后再聊StringBuffer就简单多了。因为StringBuffer同样也是继承了AbstractStringBuilder。
冬天里的懒猫
2020/08/14
4320
JDK源码解析之 Java.lang.StringBuffer
StringBuffer类继承于 AbstractStringBuilder类。实际上,AbstractStringBuilder类具体实现了可变字符序列的一系列操作,比如:append()、insert()、delete()、replace()、charAt()方法等。值得一提的是,StringBuilder也是继承于AbstractStringBuilder类。
栗筝i
2022/12/01
2130
String、StringBuilder和StringBuffer
在之前的文章「 Java 中 String 类为什么要设计成不可变的?」中对 String 的特性已经作了总结。这篇文章主要介绍另外两个常用的类 StringBuilder 和 StringBuffer 的特性。
用户3596197
2018/10/15
4750
JDK源码解析之 Java.lang.StringBuilder
StringBuilder类继承于 AbstractStringBuilder类。实际上,AbstractStringBuilder类具体实现了可变字符序列的一系列操作,比如:append()、insert()、delete()、replace()、charAt()方法等。值得一提的是,StringBuffer也是继承于AbstractStringBuilder类。
栗筝i
2022/12/01
2760
String/StringBuffer/StringBuilder的区别?
String是不可变的,StringBuilder和StringBuffer都是可变的。
黑洞代码
2021/01/14
3940
String/StringBuffer/StringBuilder的区别?
StringBuilder/StringBuffer源码阅读笔记
在 Java String类源码阅读笔记 里学习了String类的源码,StringBuilder、StringBuffer是经常拿来和String类做对比的两个类,可谓是“爱恨纠缠” ,这里我们继续学习这两个类的源码。
三分恶
2020/09/28
4400
StringBuilder/StringBuffer源码阅读笔记
【源码分析】String、StringBuffer、StringBuilder三者区别。
前段时间在面试的过程中发现,String,StringBuffer,StringBuilder的区别这个问题几乎是面试必问的题,而且在以后的开发中使用的频率极高,懂得底层原理对以后的开发效率会有大大的提高,所以在此进行总结。
百思不得小赵
2022/12/01
2340
【源码分析】String、StringBuffer、StringBuilder三者区别。
教妹学 Java 第 37 讲:字符串拼接
“哥,你让我看的《Java 开发手册》上有这么一段内容:循环体内,拼接字符串最好使用 StringBuilder 的 append() 方法,而不是 + 号操作符。这是为什么呀?”三妹疑惑地问。
沉默王二
2021/07/16
3390
Java中的String、StringBuffer和StringBuilder
作为作为一个已经入了门的java程序猿,肯定对Java中的String、StringBuffer和StringBuilder都略有耳闻了,尤其是String 肯定是经常用的。但肯定你有一点很好奇,为什么java中有三个关于字符串的类?一个不够吗!先回答这个问题,黑格尔曾经说过——存在必合理,单纯一个String确实是不够的,所以要引入StringBuffer。再后来引入StringBuilder是另一个故事了,后面会详细讲到。   要了解为什么,我们就得先来看下这三者各自都有什么样的特点,有什么样的异同,对其知根知底之后,一切谜团都会被解开。
xindoo
2021/01/21
5120
参加了这么多面试,还是不懂StringBuffer和StringBuilder的区别?
在实际开发中使用 String 类会存在一个问题,String 对象一旦创建,其值是不能修改的,如果要修改,会重新开辟内存空间来存储修改之后的对象,即修改了 String 的引用。因为 String 的底层是用数组来存值的,数组长度不可改变这一特性导致了上述问题,所以如果开发中需要对某个字符串进行频繁的修改,使用 String 就不合适了,会造成内存空间的浪费,如何解决这个问题呢?
南风
2019/09/25
4760
参加了这么多面试,还是不懂StringBuffer和StringBuilder的区别?
面经手册 · 第11篇《StringBuilder 比 String 快?空嘴白牙的,证据呢!》
面我的题开发都用不到,你为什么要问?可能这是大部分程序员求职时的经历,甚至也是大家讨厌和烦躁的点。明明给的是拧螺丝的钱、明明做的是写CRUD的事、明明担的是成工具的人!
小傅哥
2020/09/24
5570
面经手册 · 第11篇《StringBuilder 比 String 快?空嘴白牙的,证据呢!》
StringBuffer与StringBuilder的区别_String
因为String是不可变的,StringBuffer 和 StringBuilder 它们都是可变的字符串,不过它们之间的区别是 Java 初中级面试出现几率十分高的一道题。这么简单的一道题,栈长在最近的面试过程中,却经常遇到很多求职者说反,搞不清使用场景的情况。
全栈程序员站长
2022/09/29
2850
StringBuffer与StringBuilder的区别_String
Java中String、StringBuffer、StringBuilder的区别
Java中String、StringBuffer、StringBuilder的区别 1.从是否可变的角度   String类中使用字符数组保存字符串,因为有“final”修饰符,所以String对象是不可变的。 /** The value is used for character storage. */ private final char value[];   StringBuffer和StringBuilder都继承自AbstractStringBuilder类,在AbstractSt
nnngu
2018/03/15
6410
由String,String Builder,String Buffer 引起的面试惨案
String,StringBuilder,StringBuffer的区别是啥?这个面试题估计每个JAVA都应该碰到过吧。依稀记得第一次面试的时候,面试官问我这个问题时,心想着能有啥区别不都是拼接字符串嘛。深入了解这个问题后,发现并不简单?
不一样的科技宅
2020/09/02
8820
由String,String Builder,String Buffer 引起的面试惨案
StringBuilder和StringBuffer的区别
当接触这个问题的时候,我们可能第一反应就是,StringBuilder是线程不安全的,StringBuffer是线程安全的
全栈程序员站长
2022/09/07
3320
StringBuilder和StringBuffer的区别
String、StringBuilder以及StringBuffer
参考于:https://www.cnblogs.com/dolphin0520/p/3778589.html
收心
2022/01/14
2780
String、StringBuilder以及StringBuffer
推荐阅读
相关推荐
StringBuffer 和 StringBuilder 的 3 个区别!
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档