简介
- StringBuffer是
Java1.0
的API,StringBuilder是Java1.5
的API - StringBuffer和StringBuilder都是
继承自AbstractStringBuilder
- 区别在于
StringBuffer是线程安全的
,StringBuilder是线程不安全的
,前者安全但效率低,后者高效但不安全,它们的扩容机制也是这样的区别
所以我们只需要分析一个的扩容就可以了,分析StringBuffer,另一个只用把synchronized关键字去掉就是一样的,本文是基于Java1.8源码讲解
的,与1.7略有不同,但原理是一致的
String 与 CharSequence区别
- String 继承于CharSequence,也就是说String也是CharSequence类型。
- CharSequence是一个接口,它只包括
length()
,charAt(int index)
,subSequence(int start, int end)
这几个API接口。 - 除了String实现了CharSequence之外,
StringBuffer和StringBuilder
也实现了CharSequence接口。 - CharSequence就是字符串,String, StringBuilder和StringBuffer本质上都是通过字符数组实现的!
- CharSequence与String都能用于定义字符串,但CharSequence的值是
可读可写字符串
,而String的值是只读字符串
。
正文
- StringBuffer构造方法(
初始容量
)
既然是容器就一定会有个初始容量的,目的在于避免在内存中过度占用内存
.容器的初始容量有默认
和使用构造方法申明
两种.
方法 | 说明 |
---|---|
StringBuffer() | 构造一个其中不带字符的字符串缓冲区,其初始长度 为16 个字符。 |
StringBuffer(CharSequence seq) | 构造一个字符串缓冲区,它包含与指定的 CharSequence 相同的字符。 |
StringBuffer(int capacity) | 构造一个不带字符,但具有指定初始长度 的字符串缓冲区。 |
StringBuffer(String str) | 构造一个字符串缓冲区,并将其内容初始化为指定的字符串内容。其初始长度为 当前字符长度 + 16 |
-
不声明长度,使用无参构造方法创建实例对象时,会调用
父类的构造方法
,默认长度为16
//StringBuffer部分源码,StringBuffer继承了AbstractStringBuilder public final class StringBuffer extends AbstractStringBuilder implements java.io.Serializable, CharSequence{ public StringBuffer() { super(16);//调用父类构造方法 } }
//父类AbstractStringBuilder部分源码 abstract class AbstractStringBuilder implements Appendable, CharSequence { AbstractStringBuilder(int capacity) {//父类构造方法 value = new char[capacity]; } //........... }
通过上面源码可以看出,
默认容量
实际上就是创建了一个长度16的字符数组
; -
使用有参构方法传入字符串,初始化实例对象时,初始长度为
当前字符串长度 + 16
//StringBuffer部分源码 public StringBuffer(int capacity) { super(capacity);//调用父类构造方法 } public StringBuffer(String str) { super(str.length() + 16);//调用父类构造方法 append(str); } public StringBuffer(CharSequence seq) { this(seq.length() + 16); append(seq); }
可以看出
有参构造方法
有3个- 直接声明长度的,底层是根据传入长度来创建了一个字符数组,
- 使用字符串的创建呢,底层的数组长度是
字符串长度+16
- 如果是用字符序列那么和字符串(
字符串长度+16
)是一样的,然后执行append
操作.
-
StringBuffer的原理
StringBuffer本质
上是一个可变字符数组
,不像String,String是一个 final修饰的char[] 的不可变字符数组
并且它继承
了抽象类AbstractStringBuilder
,在AbstractStringBuilder类中,有`三个字段char[] value;// 保存字符 int count;// 已有字符长度 private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;// 最大数组大小
value
表示存储字符
,count
表示数组中已有内容的长度
,MAX_ARRAY_SIZE
表示最大的分配容量,即2^31-8
,若分配长度超过最大容量将会报OutOfMemoryError
的错误:请求的数组大小超过VM限制
StringBuffer的主要操作有
append、insert
等,这些操作都是在value
上进行的,而不是像String一样每次操作都要new一个String,因此,StringBuffer在效率上要高于String。有了append、insert等操作,value的大小就会改变,那么StringBuffer是如何操作容量的改变的呢?
首先StringBuffer有个继承自AbstractStringBuilder类
的ensureCapacity
的方法:
StringBuffer对其进行了重写,通过super
直接调用父类的expandCapacity
方法。//StringBuffer方法 @Override public synchronized void ensureCapacity(int minimumCapacity) { super.ensureCapacity(minimumCapacity); }
//AbstractStringBuilder部分源码 @Native public static final int MAX_VALUE = 0x7fffffff; private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8; //如果传入的`minimunCapacity`大于 0 public void ensureCapacity(int minimumCapacity) { if (minimumCapacity > 0) ensureCapacityInternal(minimumCapacity); } //如果传入的`minimunCapacity`比原来`value`的长度大,就会调用`newCapacity()` private void ensureCapacityInternal(int minimumCapacity) { // overflow-conscious code if (minimumCapacity - value.length > 0) { //通过Arrays.copyOf(原数组,新长度)返回一个新长度数组 如果长度超过原数组的长度,则保留数组默认值 value = Arrays.copyOf(value, newCapacity(minimumCapacity)); } } private int newCapacity(int minCapacity) { // overflow-conscious code //将新容量扩大到value的长度乘以2加2 (1*value^2)+ 2 int newCapacity = (value.length << 1) + 2; //新容量大小 小于 传入容量 minCapacity ,将新容量变成实际需要容量大小 if (newCapacity - minCapacity < 0) { newCapacity = minCapacity; } //如果不小于 //如果新容量大小<=0 或者 最大容量大小 小于 新容量大小,则创建新容量 return (newCapacity <= 0 || MAX_ARRAY_SIZE - newCapacity < 0) ? hugeCapacity(minCapacity) : newCapacity; } private int hugeCapacity(int minCapacity) { //如果最大容量大小 小于 实际需要容量大小,抛出OOM if (Integer.MAX_VALUE - minCapacity < 0) { // overflow throw new OutOfMemoryError(); } //如果不小于 //如果实际需要容量大小 大于最大容量大小则返回 实际需要容量大,否则返回最大容量大小 return (minCapacity > MAX_ARRAY_SIZE) ? minCapacity : MAX_ARRAY_SIZE; }
扩容策略(2*n + 2)
先将新容量扩大到value的长度乘以2加2
,如果不够则直接扩充到所需的容量大小,够则还要进行判断。如果比Integer的最大值还要大会抛异常;否则,如果比MAX_ARRAY_SIZE
大,则创建新容量为minCapacity
;如果比MAX_ARRAY_SIZE
小,则新容量为MAX_ARRAY_SIZ
public static void main(String[] args) {
//1:调用无参数构造器
StringBuffer str = new StringBuffer();
str.append("12345");
System.out.println(str.capacity());//16
System.out.println(str.length());//5
str.append("67890123456");
System.out.println(str.capacity());//16
System.out.println(str.length());//16
str.append("1");
System.out.println(str.capacity());//34
System.out.println(str.length());//17
//2:调用有参数构造器
str = new StringBuffer("123");
System.out.println(str.capacity());//19
System.out.println(str.length());//3
}