java StringBuilder 和 StringBuffer 万字详解(深度讲解)

目录

一、前言

二、StringBuffer类介绍和溯源

1.介绍 :

2.溯源 :

三、StringBuffer类常用构造器

1.StringBuffer()

2.StringBuffer(int capacity)

3.StringBuffer(String str)

4.演示

5.Debug

四、StringBuffer VS String类(重要)

1.StringBuffer类与String类的比较 :

2.StringBuffer类与String类的相互转化 :

五、StringBuffer类常用方法

0.前言

1.int length()

2.int capacity()

3.StringBuffer append(...)

4.StringBuffer delete(int start, int end)

5.StringBuffer replace(int start, int end, String str)

6.StringBuffer reverse()

7.StringBuffer insert(int offset, String str)

8.演示

六、StringBuilder介绍和溯源

1.介绍

2.溯源

七、StringBuilder类常用构造器

1.StringBuilder()

2.StringBuilder(int capacity)

3.StringBuilder(String str)

4.演示

八、StringBuilder类常用方法

0.前言

1.演示 :

九、String类,StringBuffer类,StringBuilder类总比较

十、总结


一、前言

  • 本节内容是我们《API-常用类》专题的第三小节了。本节内容主要讲StringBuffer类和StringBuilder类, 内容包括但不限于 StringBuffer介绍和溯源StringBuffer类构造器和常用方法StringBuffer类和String类的比较及相互转化,以及 StringBuilder类和StringBuffer类的比较等等。up希望通过这篇博文的知识分享,能够帮助大家快速上手并理解java StringBuffer类和StringBuilder类。
  • 注意事项 : 代码中的注释也很重要不要眼高手低,自己敲一遍才能知道怎么用点击侧边栏目录或者文章开头的目录可以跳转
  • 良工不示人以朴,up所有文章都会适时改进。大家如果有什么问题,都可以在评论区一块儿交流,或者私信up。 感谢阅读!

二、StringBuffer类介绍和溯源

        1.介绍 :

                在上一小节的String类中,我们提到,每个字符串对象都是常量。当我们创建一个字符串对象,并试图对其内容进行“增”,“删”,或者“改”的操作时,实际上原来的字符串对象已经丢弃了。jvm会重新创建一个字符串对象,并令其指向常量池中新的数据空间。所以,如果多次进行这些“增删改”的操作,会导致大量副本字符串对象遗留在内存中,降低效率。那我们如何解决这个问题?这便要引出我们的StringBuffer类和StringBuilder类。

                StringBuffer类,指可变字符序列,用于构造字符串对象。其内部使用自动扩容的数组来操作字符串数据StringBuffer类属于java.base模块,java.lang包下,如下图所示 :

        2.溯源 :

                我们先来看看StringBuffer类的源码,试试能不能从中找出一些蛛丝马迹。如下 :

                可以看到,同String类一样,StringBuffer类也用了final关键字修饰,因此,StringBuffer类也不可被继承。我们再来看一下StringBuffer类的类图,如下 :

                可以看到,StringBuffer类并没有像String类一样直接继承了Object类,而是直接继承自AbstractStringBuilder类。但它也像String类一样实现了多个接口,其中Serializable接口的实现使得StringBuffer类的对象可以串行化,串行化后对象可以进行网络传输,也可以保存到文件

                但是,这时候可能就要有p小将(Personable小将,指风度翩翩的人)出来bb问了:你丫的,之前在String类的源码中,可以明明白白地看到 “private final byte[] value” 属性,并且源码中给出了注释——字符串在底层就是用这个字节数组来存储的。那你这StringBuffer类也没有见数组啥的属性,你上哪儿存储捏?

                不愧是p小将,6。是的,与String类一个较大的不同点在于,StringBuffer类本身并没有用来存储字符串的容器。不急,刚刚在类图中我们也看见了,StringBuffer类直接继承自AbstractStringBuilder类,java这么牛逼的语言,不会让你凭空去继承这么一个类的。来看看父类的源码,如下 :

                一看父类源码咱就懂了。唉哟,藏的还挺深儿滴。没错,父类AbstractStringBuilder源码中有byte[] value属性,并且源码中也明确给出了注释 “The value is used for character storage.”,但与String类不同的是,该数组无final修饰!  因此,StringBuffer字符串实际存放的位置是在堆内存中。这也从根本上解释了为什么StringBuffer是可变字符序列。

                当然,我们也可以通过Debug找到更令人信服的证据,如下图所示 :

                AbstractStringBuilder类中的byte[] value只是定义了一个字节数组,数组属于引用类型,默认指向为空(即null),但是当我们通过构造器 "StringBuffer(String str)" 来初始化一个非空的StringBuffer类对象时,很明显在底层有一个”new“的操作(即上图中)。在java面向对象专题我们说过,new出来的对象都在堆内存中。

                不止于此,如果我们是使用空参构造初始化StringBuffer类对象,底层会通过 "super(16);" 调用父类的一个带参构造[由此可知,使用空参构造初始化StringBuffer类对象时,底层的byte[]数组默认初始容量 = 16],如下图所示 :

                大家有兴趣可以自己下来去Debug一下。只要你能大致的看懂源码,明白它是干什么的,你就能对外面显式的一些功能理解地更深,更透彻。因此,Debug这时候便显得越来越关键。(PS : 大家有兴趣可以去看看up 的Debug入门教学)。等我们到了下一专题:《API-常用工具》专题的集合篇章,up会带大家将经典的几个集合实现类例如ArrayList,HashSet等一一进行Debug调试,分析它们的底层机制。


三、StringBuffer类常用构造器

        1.StringBuffer()

                构造一个不带字符的字符串缓冲区,其初始容量为16个字符。(这里提一嘴,“buffer”本身就是缓冲区,缓冲器,缓冲物“的意思。)

        2.StringBuffer(int capacity)

                构造一个不带字符,但具有指定初始容量的字符串缓冲区。即可对byte[] value的大小进行指定。

        3.StringBuffer(String str)

                构造一个字符串缓冲区,并将其内容初始化为指定字符串的内容。

        4.演示

                up以Constructor_类为演示类,代码如下 :


package csdn.knowledge.api.builder_buffer;

public class Constructor_ {
    public static void main(String[] args) {
    //演示 : 演示StringBuffer类的常用构造器
        //1.StringBuffer()
        StringBuffer stringBuffer_0 = new StringBuffer();
        System.out.println(stringBuffer_0.length());
        System.out.println(stringBuffer_0);
        System.out.println("----------");

        //2.StringBuffer(int capacity)
        StringBuffer stringBuffer_1 = new StringBuffer(141);
        System.out.println(stringBuffer_1.length());
        System.out.println(stringBuffer_1);
        System.out.println("----------");

        //3.StringBuffer(String str)
        StringBuffer stringBuffer_2 = new StringBuffer("CSDN yyds!");
        System.out.println(stringBuffer_2.length());
        System.out.println(stringBuffer_2);
    }
}

                运行结果 :

        5.Debug

                诚然,光看上面那破代码和一张糊弄人的输出结果出,我们无法直观看出三个构造器的区别,接下来up就以上面的代码为例,在第7行下一个断点,给大家把每个构造器的执行流程都Debug一下。注意:想想上面对每个构造器性质的描述,你应该知道你想在Debug过程中看到什么。

                ①第一个构造器Debug演示GIF图如下 :

                ②第二个构造器Debug演示GIF图如下 :

                ③第三个构造器Debug演示GIF图如下 :


四、StringBuffer VS String类(重要)

        1.StringBuffer类与String类的比较 :

         ①String类保存的是 字符串常量,无法直接更改字符串本身的值。String类的每次更新实际上就是更改引用指向的地址,效率较低。

                up给大家画了一张String类的内存图解,我们以下面代码为例 :


//仅作演示用,无实际意义
public static void main(String[] args) {
    String str_0 = new String("CSDN yyds");
    str_0 = new String("666");
    str_0 = "Cyan";
}

                内存图解如下 :(注意,图示main函数中,最后应该改为“str_0 => 0x0099”🙏

         StringBuffer保存的是 字符串变量,可以直接更改字符串本身的值。因为字符串 变量堆内存中,StringBuffer的每次更新实际上可以直接更新字符串的内容,不用每次更新地址,效率较高。只有在某些特殊情况下,比如说该数组预存的空间不足,需要扩容时,才创建新的对象。

                up给大家画了一张StringBuffer类的内存图解,我们以下面代码为例 :


//仅作演示用,无实际意义
public static void main(String[] args) {
    StringBuffer sf = new StringBuffer("csdnNB");
}

内存图解如下 :

        2.StringBuffer类与String类的相互转化 :

            ①String ——> StringBuffer

                方式一:

                利用上面的第三个构造器——StringBuffer(String str)

                eg :

                StringBuffer stringBuffer_0 = new StringBuffer("CSDN yyds");

                方式二:

                利用上面的第一个构造器——StringBuffer(),再利用append方法向容器中添加字符(串)

                eg :

                StringBuffer stringBuffer_1 = new StringBuffer();

                stringBuffer_1.append("Cyan_RA9");

                Δ演示 :

                up以Exchange_0类为演示类,代码如下 :


package csdn.knowledge.api.builder_buffer;

public class Exchange_0 {
    public static void main(String[] args) {
    //演示 : String ——> StringBuffer
        //方式一 : 
        StringBuffer stringBuffer_0 = new StringBuffer("CSDN yyds!");
        System.out.println(stringBuffer_0);
        //方式二 :
        StringBuffer stringBuffer_1 = new StringBuffer();
        stringBuffer_1.append("Cyan_RA9");
        System.out.println(stringBuffer_1);
    }
}

                运行结果 :

            ②StringBuffer ——> String

                方式一:

                利用StringBuffer类提供的toString方法

                eg :

                StringBuffer stringBuffer_0 = new StringBuffer("CSDN yyds");

                String str_0 = stringBuffer.toString();

                方式二:

                利用String类提供的构造器,在形参列表中直接传入一个StringBuffer类对象

                eg :

                StringBuffer stringBuffer_1 = new StringBuffer();

                String str_1 = new String(stringBuffer_1);

                Δ演示 :

                up以Exchange_1类为演示类,代码如下 :


package csdn.knowledge.api.builder_buffer;

public class Exchange_1 {
    public static void main(String[] args) {
    //演示 : StringBuffer ——> String
        //方式一 :
        StringBuffer stringBuffer = new StringBuffer("感谢大家阅读!");
        String str_0 = stringBuffer.toString();
        System.out.println(str_0);
        //方式二 :
        String str_1 = new String(stringBuffer);
        System.out.println(str_1);
    }
}

                运行结果 :


五、StringBuffer类常用方法

        0.前言

                我们可以先在IDEA的类图中查看一下StringBuffer类中的方法,看看是个什么情况。如下GIF图所示 :

                可以看到,光StringBuffer类中的方法就是巨**多了,而且旁边它爹的方法看着更多。因此,还是老规矩,up就把一些比较常见的,常用的方法比如说crud(增删改查)给大家分享出来,并给大家演示一下就好了。

        1.int length()

                该方法可以获取到当前StringBuffer容器中字符串的有效长度。

        2.int capacity()

                该方法可以返回当前容器的容量。

        3.StringBuffer append(...)

                该方法可以将传入的形参对应的字符串加入到当前容器中。(返回值为StringBuffer类型,可不做接收。)

        4.StringBuffer delete(int start, int end)

                该方法可以删除当前容器中指定序列部分的内容。传入的两个形参代表了删除的区间——[start, end),仍然是熟悉的前闭后开。(返回值为StringBuffer类型,可不做接收。)

        5.StringBuffer replace(int start, int end, String str)

                该方法可以将当前容器中指定序列部分的字符串替换为传入的str字符串。前两个形参的作用同delete方法的形参。最后一个形参代表你想最终替换成的字符串。(返回值为StringBuffer类型,可不做接收。)

        6.StringBuffer reverse()

                该方法可以将当前容器中的字符串反转顺序后再返回。(返回值为StringBuffer类型,可不做接收。)

        7.StringBuffer insert(int offset, String str)

                该方法可以在当前容器中字符串的指定索引处插入一段字符串,原字符串中的内容从该索引处自动后移。(返回值为StringBuffer类型,可不做接收。)

        8.演示

                up以Method_类为例,代码如下 :


package csdn.knowledge.api.builder_buffer;

public class Method_ {
    public static void main(String[] args) {
    //演示 : StringBuffer类常用方法
        //1 —— int length()
        StringBuffer strBuffer_0 = new StringBuffer("CSDN yyds!");
        System.out.println("当前字符串 = " + strBuffer_0);
        System.out.println("当前容器中字符串的有效长度为:" + strBuffer_0.length());
        System.out.println("============================================");

        //2 —— int capacity()
        StringBuffer strBuffer_1 = new StringBuffer(141);
        System.out.println("当前容器的容量是:" + strBuffer_1.capacity());
        System.out.println("============================================");

        //3 —— StringBuffer append(...)
        StringBuffer strBuffer_2 = new StringBuffer("大家好,");
        strBuffer_2.append("我是练习时长两年半的java博主——");
        strBuffer_2.append("Cyan_RA9——");
        strBuffer_2.append(6666);
        strBuffer_2.append(2333.333333);
        System.out.println("strBuffer_2容器中字符串的内容 = " + strBuffer_2);
        System.out.println("============================================");

        //4 —— StringBuffer delete(int start, int end)
        StringBuffer strBuffer_3 = new StringBuffer("小米,小红,小兰,小黑");
        System.out.println("当前字符串 = " + strBuffer_3);
        strBuffer_3.delete(0, 3);
        System.out.println("删去索引为[0, 3)的字符串后,现在的字符串 = " + strBuffer_3);
        System.out.println("============================================");

        //5 —— StringBuffer replace(int start, int end, String str)
        StringBuffer strBuffer_4 = new StringBuffer("大白 大黄 大哥 大狗");
        System.out.println("当前字符串 = " + strBuffer_4);
        strBuffer_4.replace(9, 11, "大猫");
        System.out.println("将\"大狗\"替换成\"大猫\"后,现在的字符串 = " + strBuffer_4);
        System.out.println("============================================");

        //6 —— StringBuffer reverse()
        StringBuffer strBuffer_5 = new StringBuffer("123456789");
        System.out.println("当前字符串 = " + strBuffer_5);
        strBuffer_5.reverse();
        System.out.println("颠倒字符串的顺序后,现在的字符串 = " + strBuffer_5);
        System.out.println("============================================");

        //7 —— StringBuffer insert(int offset, String str)
        StringBuffer strBuffer_6 = new StringBuffer("我叫,喜欢吃水果");
        System.out.println("当前字符串 = " + strBuffer_6);
        strBuffer_6.insert(2, "Cyan_RA9");
        System.out.println("在索引为2处插入一段字符串后,现在的字符串 = " + strBuffer_6);
    }
}

                运行结果 :


六、StringBuilder介绍和溯源

        1.介绍

                同StringBuffer一样,StringBuilder类也是一个可变的字符序列StringBuilder类提供与StringBuffer类兼容的API,因此两者在使用功能上非常相似,但是StringBuilder类不保证同步,因此StringBuilder类不是线程安全的

                StringBuilder类被设计用作StringBuffer类的一个简易替换,用在字符缓冲区被单个线程使用的时候。但在实际开发中,由于StringBuilder类效率比StringBuffer类还要高。因此,建议在满足单线程的基础上,优先使用StringBuilder类。

                StringBuilder类也属于java.base模块,java.lang包下,如下图所示 :

        2.溯源

                我们先来看看StringBuilder类的源码,看看有什么线索,如下所示 :

                可以看到,StringBuilder类也被final关键字修饰,因此StringBuilder类不可被继承。我们再来看看StringBuilder类的类图,如下 :

                大家可以通过侧边栏跳转回StringBuffer类的类图看看,up表示,不能说一模一样,但至少是完全相同😋。很明显,这俩是难兄难弟。同样的,StringBuilder类也实现了Serializable接口,使得StringBuilder类对象串行化,串行化后,对象可以进行网络传输,也可以保存到文件。同样的,StringBuilder类也继承了AbstractStringBuilder类,那自然也是在AbstractStringBuilder类中的byte[] value中来保存字符串的。


七、StringBuilder类常用构造器

        1.StringBuilder()

                构造一个不带字符的字符串缓冲区,其初始容量为16个字符

        2.StringBuilder(int capacity)

                构造一个不带字符,但具有指定初始容量的字符串缓冲区。即可对byte[] value的大小进行指定。

        3.StringBuilder(String str)

                构造一个字符串缓冲区,并将其内容初始化为指定字符串的内容。

        4.演示

                up以Constructor_EX类为演示类,代码如下 :


package csdn.knowledge.api.builder_buffer.builder;

public class Constructor_EX {
    public static void main(String[] args) {
    //演示 : StringBuilder类常用构造器
        //1 —— StringBuilder()
        StringBuilder sb_0 = new StringBuilder();
        System.out.println("当前sb_0容器的容量 = " + sb_0.capacity());
        System.out.println("当前sb_0容器内字符串的有效长度 = " + sb_0.length());
        System.out.println("---------------------");
        
        //2 —— StringBuilder(int capacity)
        StringBuilder sb_1 = new StringBuilder(141);
        System.out.println("当前sb_1容器的容量 = " + sb_1.capacity());
        System.out.println("当前sb_1容器内字符串的有效长度 = " + sb_1.length());
        System.out.println("---------------------");
        
        //3 —— StringBuilder(String str)
        StringBuilder sb_2 = new StringBuilder("CSDN yyds!");
        System.out.println("当前sb_2容器的容量 = " + sb_2.capacity());
        System.out.println("当前sb_2容器内字符串的有效长度 = " + sb_2.length());
    }
}

                运行结果 :


八、StringBuilder类常用方法

        0.前言

                由于StringBuilder类使用和StringBuffer类兼容的API,因此,这两者的常用方法基本相同。至少上文中StringBuffer类的7个常用方法均可以在StringBuilder类的API文档中查找到。而且,有些眼尖的小伙伴儿刚刚可能已经发现了,StringBuilder的三个常用构造器与StringBuffer类的如出一辙。这也是up为什么没有再给出StringBuilder类构造器的Debug测试。因为就算你Debug一下,也会发现它们底层其实都一样。有兴趣的小伙伴儿们可以自己下去Debug一下。

                因为两者的常用方法都一样,基本上就换了个名字,因此up也不全演示一遍了,就挑几个典型的给大家演示一下,过过眼就行,以免影响大家阅读体验。(绝b不是因为我懒!

        1.演示 :

                up以Method_EX为演示类,代码如下 :


package csdn.knowledge.api.builder_buffer.builder;

public class Method_EX {
    public static void main(String[] args) {
    //演示 : StringBuilder类常用方法
        StringBuilder sb = new StringBuilder("12345");
        System.out.println("当前字符串 = " + sb);

        sb.reverse();
        System.out.println("颠倒后的字符串 = " + sb);

        sb.append(123);
        sb.append("哈哈哈");
        sb.append(666.666);
        sb.append("牛逼!");
        System.out.println("增加后的字符串 = " + sb);

        sb.delete(0, sb.length());
        System.out.println("全部删光光!当前字符串 = " + sb);
    }
}

                运行结果 :


九、String类,StringBuffer类,StringBuilder类总比较

                特性对比: 

String : 不可变字符序列,效率低,但是复用率高。
StringBuffer : 可变字符序列,效率较高,且线程安全。
StringBuilder : 可变字符序列,效率最高,但线程不安全。

                使用场景对比:  

String : 适用于字符串很少被修改,且被多个对象引用的情况,比如定义数据库的IP信息,配置信息等。
StringBuffer : 适用于存在大量修改字符串的情况,且满足 多线程条件。
StringBuilder : 适用于存在大量修改字符串的情况,且满足 单线程条件。

十、总结

  • 🆗,以上就是关于StringBuffer类和StringBuilder类的全部内容了。希望这篇博文的内容分享,可以帮助大家对StringBuffer 和 StringBuilder有进一步的认识。
  • 回顾一下,up从“介绍和溯源”,“常用构造器”,“常用方法”三个主要方面分别对StringBuffer 和 StringBuilder作了解释和讲解;其中在StringBuffer部分,我们还着重介绍了String类和StringBuffer类的比较;最后,我们还对String类,StringBuffer类和StringBuilder类这三个作了比较性的总结。
  • 《API-常用类》专题下一小节——up准备来讲讲常用类Math类 和 System类,我们不见不散。感谢阅读!

        System.out.println("END--------------------------------------"); 

  • 17
    点赞
  • 37
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 3
    评论
String 对象是不可改变的。每次使用 System.String 类中的方法之一时,都要在内存中创建一个新的字符串对象,这就需要为该新对象分配新的空间。在需要对字符串执行重复修改的情况下,与创建新的 String对象相关的系统开销可能会非常昂贵。如果要修改字符串而不创建新的对象,则可以使用System.Text.StringBuilder 类。例如,当在一个循环中将许多字符串连接在一起时,使用 StringBuilder类可以提升性能。 通过用一个重载的构造函数方法初始化变量,可以创建 StringBuilder 类的新实例,正如以下示例中所阐释的那样。 设置容量和长度 虽然 StringBuilder 对象是动态对象,允许扩充它所封装的字符串中字符的数量,但是您可以为它可容纳的最大字符数指定一个值。此值称为该对象的容量,不应将它与当前 StringBuilder 对象容纳的字符串长度混淆在一起。例如,可以创建 StringBuilder 类的带有字符串“Hello”(长度为 5)的一个新实例,同时可以指定该对象的最大容量为 25。当修改 StringBuilder 时,在达到容量之前,它不会为其自己重新分配空间。当达到容量时,将自动分配新的空间且容量翻倍。可以使用重载的构造函数之一来指定 StringBuilder 类的容量。以下代码示例指定可以将 MyStringBuilder 对象扩充到最大 25 个空白。 MyStringBuilder.Capacity = 25; EnsureCapacity 方法可用来检查当前 StringBuilder 的容量。如果容量大于传递的值,则不进行任何更改;但是,如果容量小于传递的值,则会更改当前的容量以使其与传递的值匹配。 也可以查看或设置 Length 属性。如果将 Length 属性设置为大于 Capacity 属性的值,则自动将Capacity 属性更改为与 Length 属性相同的值。如果将 Length 属性设置为小于当前 StringBuilder 对象内的字符串长度的值,则会缩短该字符串。 修改 StringBuilder 字符串 下表列出了可以用来修改 StringBuilder 的内容的方法。 StringBuilder.Append 将信息追加到当前 StringBuilder 的结尾。 StringBuilder.AppendFormat 用带格式文本替换字符串中传递的格式说明符。 StringBuilder.Insert 将字符串或对象插入到当前 StringBuilder 对象的指定索引处。 StringBuilder.Remove 从当前 StringBuilder 对象中移除指定数量的字符。 StringBuilder.Replace 替换指定索引处的指定字符。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Cyan_RA9

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值