String,StringBuilder,StringBuffer 实现原理解析

定义:
从jdk1.5开始提供的新的封装字符串的类,StringBuilder,其字符串拼接操作的效率远远高于 String。

Java里面提供了String,StringBuffer和StringBuilder三个类来封装字符串

简介:
我们知道字符串其实就是由若干个字符线性排列而成的,可以理解为字符数组Array,那么既然是数组实现的,那就需要考虑到数组的特性,数组在内存中是一块连续的地址空间块,即在定义数组的时候需要指定数组的大小

换言之, 数组就分为可变数组和不可变数组。可变数组能够动态插入和删除,而不可变数组一旦分配好空间后则不能进行动态插入或删除操作。

在实际的字符串应用场景中,涉及到多种操作,比如字符串的插入,删除,修改,拼接,查询,替换...

String:
不可变类,属性value为不可变数组,即String初始化构造器没有初始容量为16的概念,你定义多少,String中字符数组的长度就是多少,不存在字符数组扩容一说。看下源码:

String,StringBuilder,StringBuffer 实现原理解析_第1张图片
String,StringBuilder,StringBuffer 实现原理解析_第2张图片

final修饰的String 类,以及final修饰的char[] value,表示String类不可被继承,且value只能被初始化一次。这里的value变量其实就是存储了String字符串中的所有字符。

那既然String,不可变。我们再看下它的截取方法subString()实现

String,StringBuilder,StringBuffer 实现原理解析_第3张图片

这里可以看到,在substring方法中,如果传入的参数为0,就返回自身原对象,否则就是重新创建一个新的对象。

String,StringBuilder,StringBuffer 实现原理解析_第4张图片
String,StringBuilder,StringBuffer 实现原理解析_第5张图片

类似的我们可以看到,String类的concat方法,replace方法,都是内部重新生成一个String对象的。

这也就是为什么我们如果采用String对象频繁的进行拼接,截取,替换操作效率很低下的原因。

下面再看下StringBuilder对象的源码,分析为何其在做字符串的拼接,截取,替换方面效率远远高于String

StringBuilder:
内部可变数组,存在初始化StringBuilder对象中字符数组容量为16,存在扩容。

String,StringBuilder,StringBuffer 实现原理解析_第6张图片

StringBuilder类继承AbstractStringBuilder抽象类,其中StringBuilder的大部分方法都是直接调用的父类的实现。

首先看下StringBuilder的构造方法

String,StringBuilder,StringBuffer 实现原理解析_第7张图片

1:空参数的构造方法

String,StringBuilder,StringBuffer 实现原理解析_第8张图片
String,StringBuilder,StringBuffer 实现原理解析_第9张图片

2:自定义初始容量-构造函数

String,StringBuilder,StringBuffer 实现原理解析_第10张图片

3:以字符串String 作为参数的构造

String,StringBuilder,StringBuffer 实现原理解析_第11张图片

在参数Str 数组长度的基础上再增加16个字符长度,作为StringBuilder实例的初始数组容量,并将str字符串 append到StringBuilder的数组中。

String,StringBuilder,StringBuffer 实现原理解析_第12张图片

具体看下父类AbstractStringBuilder的append方法

String,StringBuilder,StringBuffer 实现原理解析_第13张图片

1:首先判断append的参数是否为null,如果为null的话,这里也是可以append进去的

String,StringBuilder,StringBuffer 实现原理解析_第14张图片

其中ensureCapacityInternal方法是确保这次append 的时候StringBuilder的内部数组容量是满足的,即这次要append的null字符长度为4,加上之前内部数组中已有的字符位数c之后作为参数执行。

String,StringBuilder,StringBuffer 实现原理解析_第15张图片

2:如果不为null的话,就获取这次需要append的str的字符长度。紧接着执行是否需要扩容的方法

3:重点看下append方法的关键:String的 getChars方法(从str的0位开始,到str的长度,当前StringBuilder对象的字符数组,当前数组已有的字符长度)

String,StringBuilder,StringBuffer 实现原理解析_第16张图片
String,StringBuilder,StringBuffer 实现原理解析_第17张图片

其实是调用了System的arraycopy方法 参数如下:

value 为str的内部不可变字符数组,

srcBegin 为从str 字符串数组的0下标开始,

srcEnd 为str字符串数组的长度,

dst 为StringBuilder对象的内部可变字符数组,

dstBegin 则为StringBuilder对象中已有的字符长度(char[] 已有的元素长度)

即整个StringBuilder的append方法,本质上是调用System的native方法,直接将String 类型的str字符串中的字符数组,拷贝到了StringBuilder的字符数组中

String,StringBuilder,StringBuffer 实现原理解析_第18张图片

toString():
最后说下StringBuilder的toString方法,
String,StringBuilder,StringBuffer 实现原理解析_第19张图片

这里的toString方法直接new 一个String对象,将StringBuilder对象的value进行一个拷贝,重新生成一个对象,不共享之前StringBuilder的char[]

以上就是StringBuilder的拼接字符串的原理分析,可以发现没有像String一样去重新new 对象,所以在频繁的拼接字符上,StringBuilder的效率远远高于String类。

StringBuffer:
线程安全的高效字符串操作类,看下源码:

String,StringBuilder,StringBuffer 实现原理解析_第20张图片

类图和StringBuilder一样,不多说

构造函数:
String,StringBuilder,StringBuffer 实现原理解析_第21张图片

和StringBuilder一样,也不用多说,重点看下其append方法:
String,StringBuilder,StringBuffer 实现原理解析_第22张图片

可以看到这里就是在append方法上加了同步锁,来实现多线程下的线程安全。其他的和StringBuilder一致。

这里比StringBuilder多了一个参数

String,StringBuilder,StringBuffer 实现原理解析_第23张图片

这里的作用简单介绍一下,就是去缓存toString的

可以看下StringBuffer的toString方法

String,StringBuilder,StringBuffer 实现原理解析_第24张图片

这里的作用就是如果StringBuffer对象此时存在toStringCache,在多次调用其toString方法时,其new出来的String对象是会共享同一个char[] 内存的,达到共享的目的。但是StringBuffer只要做了修改,其toStringCache属性值都会置null处理。这也是StringBuffer和StringBuilder的一个区别点。

总结:
String 类不可变,内部维护的char[] 数组长度不可变,为final修饰,String类也是final修饰,不存在扩容。字符串拼接,截取,都会生成一个新的对象。频繁操作字符串效率低下,因为每次都会生成新的对象。

StringBuilder 类内部维护可变长度char[] , 初始化数组容量为16,存在扩容, 其append拼接字符串方法内部调用System的native方法,进行数组的拷贝,不会重新生成新的StringBuilder对象。非线程安全的字符串操作类, 其每次调用 toString方法而重新生成的String对象,不会共享StringBuilder对象内部的char[],会进行一次char[]的copy操作。

StringBuffer 类内部维护可变长度char[], 基本上与StringBuilder一致,但其为线程安全的字符串操作类,大部分方法都采用了Synchronized关键字修改,以此来实现在多线程下的操作字符串的安全性。其toString方法而重新生成的String对象,会共享StringBuffer对象中的toStringCache属性(char[]),但是每次的StringBuffer对象修改,都会置null该属性值。

你可能感兴趣的:(java)