Java源码深度分析String与StringBuffer及StringBuilder详解

 更新时间:2022年05月11日 10:07:15   作者:星辰与晨曦  
当对字符串进行修改的时候,需要使用 StringBuffer 和 StringBuilder类,和String类不同的是,StringBuffer和 StringBuilder类的对象能够被多次的修改,并且不产生新的未使用对象,本篇我们来分析分析它们的源码

String的字符串是不可变的,StringBuffer和StringBuilder是可变的

String:是字符常量,适用于少量的字符串操作的情况。

StringBuilder:适用于单线程下在字符缓冲区进行大量操作的情况 。

StringBuffer:适用多线程下在字符缓冲区进行大量操作的情况。

StringBuffer和StringBuild的区别

StingBuffer是线程安全的,StringBuild是线程不安全的。

然后再往上查看,就会发现他两都是调用了AbstractStringBuilder类来实现的。

我们不是说StringBuffer和StringBuilder创建的字符串是可变的。

至于源码就直接看源码,就直接在源码当中详细的体现出来了。

在这篇博客我主要说一下StringBuffer,因为StingBuild和StringBuffer的方法几乎一样,我在这就不做过多的详细赘述了。

创建StringBuffer()

第一种创建方法:

StringBuffer stringBuffer = new StringBuffer();

在使用这个方法创建的Buffer字符串的时候,看底层代码它就调用了这个方法:

    public StringBuffer() {
        super(16);
    }

它虽然没有给传入参数,它是它默认的是一个数组长度为16的字符串。

在往上看这个向上传的父类方法就更加简单明了:

    AbstractStringBuilder(int capacity) {
        value = new char[capacity];
    }

直接创建长度为16的字符型数组。

第二种创建方法:

StringBuffer stringBuffer = new StringBuffer(5);

直接给定要创建的字符长度。

底层源码:

    public StringBuffer(int capacity) {
        super(capacity);
    }

第三种创建方法:

StringBuffer stringBuffer = new StringBuffer(“Xin”);

在创建的时候,初始给定初始化的一个字符串。

那么在这个时候就要考虑它的底层保存字符串的数组要创建几位呢? 看源码,源码会告诉我们一切的。

    public StringBuffer(String str) {
        super(str.length() + 16);
        append(str);
    }

源码当中明确的告诉了我们,在这种情况下创建的字符串,底层数组长度是初始字符串的长度加16.

小小的总结一下:

在创建StringBuffer或StringBuilder字符串的时候如果没有给长度,默认为16的长度,给长度的话,就是给定的长度,给的是字符串的话,默认初始长度就是字符串的长度加16

添加功能

public StringBuffer append(String str)

代码演示一下,这还要一个点考虑一下:

public class Demo01 {
    public static void main(String[] args) {
        StringBuffer stringBuffer = new StringBuffer();
        stringBuffer.append("Xin_");    //stringBuffer.length = 4
        stringBuffer.append("Chen_");   //stringBuffer.length = 9
        stringBuffer.append("Yu_");     //stringBuffer.length = 12
        stringBuffer.append("Chen_");   //stringBuffer.length = 17
        stringBuffer.append("Xi");      //stringBuffer.length = 19
        System.out.println(stringBuffer);
    }
}

在不停的添加下去,当数组的长度到达16的时候,要考虑一下扩容的问题,如何扩容?

老规矩,看源代码,一切都在代码里:

照着这个步骤一步一步的点进去,到达最后的源代码:

MAX_ARRAY_SIZE的长度为:

private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;

所以最后是 0x7fffffff-8

总的来说,每次底层数组扩容为上次的2倍加2的长度。

删除功能

第一种删除字符串中指定位置的字符

public StringBuffer deleteCharAt(int index)

这个指定删除字符串当中明确的第几个索引。只删除一个元素

注意这里是索引的位置,不是第几个元素。

public class Demo01 {
    public static void main(String[] args) {
        StringBuffer stringBuffer = new StringBuffer("Xin_Chen_Yu_Chen_Xi");
        System.out.println(stringBuffer);
        stringBuffer.deleteCharAt(4);
        System.out.println(stringBuffer);
    }
}

源码其实也不难,就不重视的看了,在这主要看一经过删除操作,它的字符串长度是如何减少的。

删除字符串当中指定区间的字符串

public StringBuffer delete(int start,int end)

牢记在Java当中大多情况下,数组下标索引都是左闭右开的。

在下面的代码当中,我删除的就是"Chen_"这5个字符

public class Demo01 {
    public static void main(String[] args) {
        StringBuffer stringBuffer = new StringBuffer("Xin_Chen_Yu_Chen_Xi");
        System.out.println(stringBuffer);
        stringBuffer.delete(4,9);
        System.out.println(stringBuffer);
    }
}

和上面一样。看一眼源码,如何处理删除后的字符串的长度:

它两的底层都调用的是native本地反方,交由计算机系统完成。

替换功能

就是将字符串中其中某一段的字符串替换为其他的字符串。

public StringBuffer replace(int start,int end,String str)

public class Demo01 {
    public static void main(String[] args) {
        StringBuffer stringBuffer = new StringBuffer("Xin_Chen_Yu_Chen_Xi");
        System.out.println(stringBuffer);
        stringBuffer.replace(4,8,"Gu_Chen");
        System.out.println(stringBuffer);
    }
}

这里也都是数组的下标索引,所以就是从0开始计数的。

替换这也同上面的一样,也需要考虑一下底层字符串的长度问题,先看源码:

反转功能

这个功能就是将原字符串玩去哪反转过来,倒序保存起来。

public class Demo01 {
    public static void main(String[] args) {
        StringBuffer stringBuffer = new StringBuffer("Xin_Chen_Yu_Chen_Xi");
        System.out.println(stringBuffer);
        stringBuffer.reverse();
        System.out.println(stringBuffer);
    }
}

也来看一下它的源代码

最后总结一下

  • String的字符串是不可变的,StringBuffer和StringBuilder是可变的
  • String:是字符常量,适用于少量的字符串操作的情况。
  • StringBuilder:线程不安全的,适用于单线程下在字符缓冲区进行大量操作的情况 。
  • StringBuffer:线程不安全的,适用多线程下在字符缓冲区进行大量操作的情况。

在这几条总计出来的说法当中,在这再详细说明一下为什么StringBuffer和StringBuilder是可变的。

在文章上面,我展示了许多的源码,仔细观察一下,Buffer和Builder修饰的方法,它最后返回的都是this,返回了,自己,就是无论什么操作,它最终都返回的是自己。

但是有些小伙伴还是有个疑问,当它底层扩容超过它的最大值的时候,它的数组不就要换了吗?

这点确实是对的,但是在这我们要理清一个概念,StringBuffer引用的是底层数组名字的地址,而数组在需要换一个更大的时候,这个时候是数组的引用变化了,就是指向数组的地址变了,但是指向StringBuffer的地址是不变的。

我下来使用一个debug动图来展示一下,相信能更好的理解一下。

主要看控制台里的StringBuffer和value的值。我们发现StringBuffer值地址自始至终都没有改变,而value的值在到达16的时候,需要扩容的时候,就变了。这正印证了我上面所说的。

本篇博客也就到此为止了,感谢你的阅读。如果要是还是想不清楚,特别是最后的总结部分,欢迎在评论区讨论,我看到话,会第一时间共同讨论,解决问题。

这也是我重读javase基础的一个系列,比起当时初学的时候,现在看问题多了个高度,理解什么也相对轻松一点全面一些。学习起来更加偏向阅读源码来看,所以多为大家分享看源码。

到此这篇关于Java源码深度分析String与StringBuffer及StringBuilder详解的文章就介绍到这了,更多相关Java String源码分析内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • Java实现Map集合遍历的四种常见方式与用法分析

    Java实现Map集合遍历的四种常见方式与用法分析

    这篇文章主要介绍了Java实现Map集合遍历的四种常见方式与用法,结合实例形式较为详细的分析了java针对Map集合键值对遍历的常见使用技巧与相关操作注意事项,需要的朋友可以参考下
    2018-01-01
  • Java中Integer类型值相等判断方法

    Java中Integer类型值相等判断方法

    这篇文章主要给大家介绍了关于Java中Integer类型值相等判断的相关资料,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2021-02-02
  • Druid连接池未关闭导致内存泄漏问题

    Druid连接池未关闭导致内存泄漏问题

    这篇文章主要介绍了Druid连接池未关闭导致内存泄漏问题,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教
    2023-12-12
  • myeclipse安装Spring Tool Suite(STS)插件的方法步骤

    myeclipse安装Spring Tool Suite(STS)插件的方法步骤

    这篇文章主要介绍了myeclipse安装Spring Tool Suite(STS)插件的方法步骤,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2019-08-08
  • 微服务通过Feign调用进行密码安全认证操作

    微服务通过Feign调用进行密码安全认证操作

    这篇文章主要介绍了微服务通过Feign调用进行密码安全认证操作,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2021-06-06
  • 如何将java或javaweb项目打包为jar包或war包

    如何将java或javaweb项目打包为jar包或war包

    本文主要介绍了如何将java或javaweb项目打包为jar包或war包,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2022-07-07
  • Java中将String类型转换为int类型的几种常见方法

    Java中将String类型转换为int类型的几种常见方法

    在java中经常会遇到需要对数据进行类型转换的场景,这篇文章主要给大家介绍了关于Java中将String类型转换为int类型的几种常见方法,文中通过代码介绍的非常详细,需要的朋友可以参考下
    2024-07-07
  • Spring MVC Locale 本地化示例详解

    Spring MVC Locale 本地化示例详解

    这篇文章主要为大家介绍了Spring MVC Locale本地化示例详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2023-09-09
  • 如何解决@PutMapping或@PostMapping接收String类型参数多两个“引号问题

    如何解决@PutMapping或@PostMapping接收String类型参数多两个“引号问题

    这篇文章主要介绍了如何解决@PutMapping或@PostMapping接收String类型参数多两个“引号问题,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教
    2024-08-08
  • mybatis+springboot发布postgresql数据的实现

    mybatis+springboot发布postgresql数据的实现

    本文主要介绍了mybatis+springboot发布postgresql数据的实现,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2023-11-11

最新评论