前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >String及StringTable(四):StringBuffer与StringBuilder对比

String及StringTable(四):StringBuffer与StringBuilder对比

作者头像
冬天里的懒猫
发布于 2020-08-14 08:22:21
发布于 2020-08-14 08:22:21
41700
代码可运行
举报
运行总次数:0
代码可运行

分析完StringBuilder,然后再聊StringBuffer就简单多了。因为StringBuffer同样也是继承了AbstractStringBuilder。

1.类结构及成员变量

1.1 类结构

此时对为啥要将StringBuilder还拆分为一个抽象类AbstractStringBuilder恍然大悟了。因为StringBuffer可以复用。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
/**
 * A thread-safe, mutable sequence of characters.
 * A string buffer is like a {@link String}, but can be modified. At any
 * point in time it contains some particular sequence of characters, but
 * the length and content of the sequence can be changed through certain
 * method calls.
 * <p>
 * String buffers are safe for use by multiple threads. The methods
 * are synchronized where necessary so that all the operations on any
 * particular instance behave as if they occur in some serial order
 * that is consistent with the order of the method calls made by each of
 * the individual threads involved.
 * <p>
 * The principal operations on a {@code StringBuffer} are the
 * {@code append} and {@code insert} methods, which are
 * overloaded so as to accept data of any type. Each effectively
 * converts a given datum to a string and then appends or inserts the
 * characters of that string to the string buffer. The
 * {@code append} method always adds these characters at the end
 * of the buffer; the {@code insert} method adds the characters at
 * a specified point.
 * <p>
 * For example, if {@code z} refers to a string buffer object
 * whose current contents are {@code "start"}, then
 * the method call {@code z.append("le")} would cause the string
 * buffer to contain {@code "startle"}, whereas
 * {@code z.insert(4, "le")} would alter the string buffer to
 * contain {@code "starlet"}.
 * <p>
 * In general, if sb refers to an instance of a {@code StringBuffer},
 * then {@code sb.append(x)} has the same effect as
 * {@code sb.insert(sb.length(), x)}.
 * <p>
 * Whenever an operation occurs involving a source sequence (such as
 * appending or inserting from a source sequence), this class synchronizes
 * only on the string buffer performing the operation, not on the source.
 * Note that while {@code StringBuffer} is designed to be safe to use
 * concurrently from multiple threads, if the constructor or the
 * {@code append} or {@code insert} operation is passed a source sequence
 * that is shared across threads, the calling code must ensure
 * that the operation has a consistent and unchanging view of the source
 * sequence for the duration of the operation.
 * This could be satisfied by the caller holding a lock during the
 * operation's call, by using an immutable source sequence, or by not
 * sharing the source sequence across threads.
 * <p>
 * Every string buffer has a capacity. As long as the length of the
 * character sequence contained in the string buffer does not exceed
 * the capacity, it is not necessary to allocate a new internal
 * buffer array. If the internal buffer overflows, it is
 * automatically made larger.
 * <p>
 * Unless otherwise noted, passing a {@code null} argument to a constructor
 * or method in this class will cause a {@link NullPointerException} to be
 * thrown.
 * <p>
 * As of  release JDK 5, this class has been supplemented with an equivalent
 * class designed for use by a single thread, {@link StringBuilder}.  The
 * {@code StringBuilder} class should generally be used in preference to
 * this one, as it supports all of the same operations but it is faster, as
 * it performs no synchronization.
 *
 * @author      Arthur van Hoff
 * @see     java.lang.StringBuilder
 * @see     java.lang.String
 * @since   JDK1.0
 */
 public final class StringBuffer
    extends AbstractStringBuilder
    implements java.io.Serializable, CharSequence
{

}

其注释大意为:提供一个线程安全,可变的字符串序列。这个字符串序列可以通过一些方法进行调整。 StringBuffer对于对现场而言是安全的,因为内部所有的方法都是同步方法,通过synchronized确保了这些方法的顺序性。主要的一些操作是append和insert等方法。每当涉及到对StringBuffer源序列进行同步操作的时候,这个类仅仅执行字符缓冲区,而不是源对象。 每个StringBuffer有一个容量,只要长度不超过buffer的容量,没必要重新分配。一旦长度大于内部buffer长度,则会自动放大。 将null传递给构造函数会抛出NullPointerException。 StringBuilder与之等效,但是其效率更优,因为它不同步。

1.2 成员变量

与StringBuilder很大的不同是,在StringBuffer中,存在一个toStringCache属性。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
    /**
     * A cache of the last value returned by toString. Cleared
     * whenever the StringBuffer is modified.
     */
    private transient char[] toStringCache;

    /** use serialVersionUID from JDK 1.0.2 for interoperability */
    static final long serialVersionUID = 3388685877147921107L;

这个成员变量的意思是说在toString需要的时候,将最新的value中的内容。如果有修改的时候则将这个cache清空。

2.toString方法比较

我们在前面知道,StringBuffer比StringBuilder多了一个toStringCache数组,为什么StringBuffer的实现与StringBuilder不同呢?我们对其代码进行分析。

2.1 StringBuilder.toString
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
@Override
public String toString() {
    // Create a copy, don't share the array
    return new String(value, 0, count);
}

public String(char value[], int offset, int count) {
    if (offset < 0) {
        throw new StringIndexOutOfBoundsException(offset);
    }
    if (count <= 0) {
        if (count < 0) {
            throw new StringIndexOutOfBoundsException(count);
        }
        if (offset <= value.length) {
            this.value = "".value;
            return;
        }
    }
    // Note: offset or count might be near -1>>>1.
    if (offset > value.length - count) {
        throw new StringIndexOutOfBoundsException(offset + count);
    }
    this.value = Arrays.copyOfRange(value, offset, offset+count);
}

实际上这个还是使用的是 Arrays.copyOfRange。其底层是通过System.arraycopy进行的对象拷贝。

2.2 StringBuffer.toString
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
@Override
public synchronized String toString() {
    if (toStringCache == null) {
        toStringCache = Arrays.copyOfRange(value, 0, count);
    }
    return new String(toStringCache, true);
}

在看看String的这个构造方法:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
/*
* Package private constructor which shares value array for speed.
* this constructor is always expected to be called with share==true.
* a separate constructor is needed because we already have a public
* String(char[]) constructor that makes a copy of the given char[].
*/
String(char[] value, boolean share) {
    // assert share : "unshared not supported";
    this.value = value;
}

这个构造方法只提供了同package中的类才能访问到。 我们可以看到,实际上是一开始,在String类中定义了一个char数组,之后调用这个String比较特殊的构造函数,直接修改指针,指向这个共享的数组。 最开始我也没看明白,这个toStringCache的意义。 但是,我们试想一种情况,在多线程的情况下,如果使用了StringBuffer,且其值不发生改变的时候,就可以直接复用前一次的结果。虽然多个线程new了不同的String,但是其本质的char数组只有一个,不会重复的System.arraycopy。因此会带来性能的提升。虽然StringBuffer的toString方法本身是synchronized的,但是这样一来可以降低对象拷贝带来的性能损耗。

3.同步

StringBuffer与StringBuilder最大的区别在于同步,可以简单认为StringBuffer就是加上了synchronized的StringBuilder。实际上StringBuffer的实现也是在各个方法上加上synchronized关键字。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
    @Override
    public synchronized int length() {
        return count;
    }

    @Override
    public synchronized int capacity() {
        return value.length;
    }


    @Override
    public synchronized void ensureCapacity(int minimumCapacity) {
        super.ensureCapacity(minimumCapacity);
    }

    /**
     * @since      1.5
     */
    @Override
    public synchronized void trimToSize() {
        super.trimToSize();
    }

    /**
     * @throws IndexOutOfBoundsException {@inheritDoc}
     * @see        #length()
     */
    @Override
    public synchronized void setLength(int newLength) {
        toStringCache = null;
        super.setLength(newLength);
    }
    
        public synchronized StringBuffer append(StringBuffer sb) {
        toStringCache = null;
        super.append(sb);
        return this;
    }

    /**
     * @since 1.8
     */
    @Override
    synchronized StringBuffer append(AbstractStringBuilder asb) {
        toStringCache = null;
        super.append(asb);
        return this;
    }

可以看到所有核心的方法都是加上synchronized之后调用的super方法。 在对String中char数组有结构性变更的地方都会将toStringCache变为null。

4.总结

StringBuffer与StringBuilder的主要区别在于:

  • StringBuffer是线程安全的,适合在多线程环境下使用,StringBuilder不是线程安全的,只能在单线程下使用。
  • 二者的toString方法实现不同,StringBuffer中考虑到了多线程环境,因而进行了优化,加入了toStringCache,当StringBuffer固定不变时多线程进行范围的时候可以有效复用。不会反复调用System.arraycopy。但是这个优化对于StringBuilder实际上意义不大,StringBuilder本身不具有线程安全性。尤其是对于toString方法,一定不要混淆其使用场景。可以参考一次简单却致命的错误。该文章说明了一次由于错误使用toString方法而带来的问题。
本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2020/08/12 ,如有侵权请联系 cloudcommunity@tencent.com 删除

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

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

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

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
Java基础—String、StringBuffer、StringBuilder
本文主要介绍了Java中字符串的各种操作,包括字符串拼接、拆分、查找、替换和反转等。同时,还介绍了StringBuilder和StringBuffer的区别以及字符串缓存对性能的影响。
Java后端工程师
2017/12/13
7880
Java基础—String、StringBuffer、StringBuilder
StringBuffer 和 StringBuilder 的 3 个区别!
这么简单的一道题,栈长在最近的面试过程中,却经常遇到很多求职者说反,搞不清使用场景的情况。
Java技术栈
2019/07/08
6170
StringBuffer 和 StringBuilder 的 3 个区别!
源码超度:String、StringBuffer、StringBuilder
String、StringBuffer、StringBuilder是常用的字符序列,从源码上对比下,三者的区别
用户2146693
2021/12/28
2680
源码超度:String、StringBuffer、StringBuilder
Java String 源码分析
String 是final 类型不能被继承,同时实现了 java.io.serializable Comparable charSequence 三个接口。
王小明_HIT
2020/10/23
3840
StringBuilder,String与StringBuffer 的异同
引言 根据我在网上查到的资料显示,这三者的区别主要是: String:字符串常量 StringBuffer:字符创变量(多线程) StringBuilder:字符创变量(单线程) 对String的操作表面上看是对同一个变量的操作,但实际上是新建了一个常量,然后修改对象的引用。基于这样的机制,需要不停的GC旧的对象,其效率也很低下。 而StringBuffer与StringBuilder就不一样了,他们是字符串变量,是可改变的对象,每当我们用它们对字符串做操作时,实际上是在一个对象上操作的
用户1665735
2018/06/21
4890
String、StringBuilder、StringBuffer 用法比较
String、StringBuilder、StringBuffer 三个类源自JDK的 java/lang/ 目录下:
阳光岛主
2019/02/19
6270
JDK源码解析之 Java.lang.StringBuffer
StringBuffer类继承于 AbstractStringBuilder类。实际上,AbstractStringBuilder类具体实现了可变字符序列的一系列操作,比如:append()、insert()、delete()、replace()、charAt()方法等。值得一提的是,StringBuilder也是继承于AbstractStringBuilder类。
栗筝i
2022/12/01
2050
String及StringTable(三):StringBuilder源码解读
既然在前面章节说到java中的字符串相加,实际上是执行的StringBuilder的append操作,那么现在就对StringBuilder的相关源码进行解读。
冬天里的懒猫
2020/08/14
4370
JDK源码解析之 Java.lang.StringBuilder
StringBuilder类继承于 AbstractStringBuilder类。实际上,AbstractStringBuilder类具体实现了可变字符序列的一系列操作,比如:append()、insert()、delete()、replace()、charAt()方法等。值得一提的是,StringBuffer也是继承于AbstractStringBuilder类。
栗筝i
2022/12/01
2610
StringBuffer与StringBuilder的区别_String
因为String是不可变的,StringBuffer 和 StringBuilder 它们都是可变的字符串,不过它们之间的区别是 Java 初中级面试出现几率十分高的一道题。这么简单的一道题,栈长在最近的面试过程中,却经常遇到很多求职者说反,搞不清使用场景的情况。
全栈程序员站长
2022/09/29
2520
StringBuffer与StringBuilder的区别_String
彻底弄懂StringBuffer与StringBuilder的区别「建议收藏」
1、都继成了AbstractStringBuilder这个抽象类,实现了CharSequence接口
全栈程序员站长
2022/09/07
7.5K0
彻底弄懂StringBuffer与StringBuilder的区别「建议收藏」
Java--StringBuilder,StringBuffer,StringJoiner
开始自己的一个半年计划,也就是java相关常用类的源码阅读,通过阅读查漏补缺,加深基础知识的运用与理解.
屈定
2018/09/27
1K0
Java--StringBuilder,StringBuffer,StringJoiner
StringBuilder/StringBuffer源码阅读笔记
在 Java String类源码阅读笔记 里学习了String类的源码,StringBuilder、StringBuffer是经常拿来和String类做对比的两个类,可谓是“爱恨纠缠” ,这里我们继续学习这两个类的源码。
三分恶
2020/09/28
4330
StringBuilder/StringBuffer源码阅读笔记
String,StringBuilder和StringBuffer整理汇总
String 是 Java 语言非常基础和重要的类,提供了构造和管理字符串的各种基本逻辑。String是只读字符串,它并不是基本数据类型,而是一个对象。从底层源码来看是一个final类型的字符数组,所引用的字符串不能被改变,一经定义,无法再增删改。也由于它的不可变性,类似拼接、裁剪字符串等动作,每次对String的操作都会生成新的 String对象
joshua317
2022/03/29
3.7K0
String,StringBuilder和StringBuffer整理汇总
通过源码分析 String、StringBuffer 和 StringBuilder
0x00 简介 又翻出来了在15年整理的笔记了。感觉当初还是挺较真的。 自己对String的理解总是存在着不同程度的误差,经常处于一知半解的状态,而且对其内部的原理也不是特别清楚,碰巧又和同学聊起这个知识点,秉承爱折腾的原则,在论文答辩之际详细整理一下。 0x01 说明 最初听说的String、StringBuffer和StringBuilder三者之间的区别主要是下面这个版本(略作总结): String:字符串常量,字符串长度不可变。Java中String是immutable(不可变)的。用于存放字符的数
木东居士
2018/05/25
5460
String/StringBuffer/StringBuilder的区别?
String是不可变的,StringBuilder和StringBuffer都是可变的。
黑洞代码
2021/01/14
3710
String/StringBuffer/StringBuilder的区别?
面试题系列第8篇:谈谈String、StringBuffer、StringBuilder的区别?
关于字符串的面试题除了内存分布、equals比较,最常见的就是与StringBuffer和StringBuilder之间的区别了。
程序新视界
2020/09/17
9010
面试题系列第8篇:谈谈String、StringBuffer、StringBuilder的区别?
Java StringBuilder和StringBuffer理解
首先,由于String是final类,所以其一旦赋值是不可修改的,同时其自带方法,类似与substring,replace,等,都是对原String拷贝后的再修改,所以String的使用不是很方便,也会很费存储空间和效率。
Tim在路上
2020/08/05
4980
Java源码学习 -- java.lang.StringBuilder,java.lang.StringBuffer,java.lang.AbstractStringBuilder
YGingko
2017/12/28
1.6K0
Java数据类型—StringBuilder与StringBuffer「建议收藏」
StringBuilder与StringBuffer作用就是用来处理字符串,但String类本身也具备很多方法可以用来处理字符串,那么为什么还要引入这两个类呢?前面我们讲解到String 对象的不可变性,以及它的不足那就是创建新的对象,具体你可以查看文章String进阶之不可变性,因为它是不可变的,所以你对它的操作逻辑就体现在另外一个对象里,那就是你的操作新创建的对象。
全栈程序员站长
2022/09/07
2780
推荐阅读
相关推荐
Java基础—String、StringBuffer、StringBuilder
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
本文部分代码块支持一键运行,欢迎体验
本文部分代码块支持一键运行,欢迎体验