我们都知道,String的内部实现是一个用final的数组,因此String对象是不可变的,我们每次修改String时,实际上都是new出来了一个新的对象。因此,对于经常进行字符串的修改操作时,String类就需要不断创建新对象,性能极低。StringBuilder内部也是封装的一个字符数组,只不过该数组非final修饰,可以不断修改。所以对于一些经常需要修改字符串的情况,我们应首选StringBuilder。
StringBuilder的属性方法:
value属性表示的是一个字符数组,该数组的作用和String中的字符数组的作用是一样的,只是此value数组并没有被final修饰,也就是说该数组内部的值是可以动态修改的,这也是StringBuilder存在的意义。count属性表示的不是value数组的长度,它代表的是value数组中实际上存放的字符数目,例如:value长度为10,我存放8个字符,剩下位置为空,此时count的值就为8,而value.length()为10。
void expandCapacity(int minimumCapacity) {
int newCapacity = value.length * 2 + 2;
if (newCapacity - minimumCapacity < 0)
newCapacity = minimumCapacity;
if (newCapacity < 0) {
if (minimumCapacity < 0) // overflow
throw new OutOfMemoryError();
newCapacity = Integer.MAX_VALUE;
}
value = Arrays.copyOf(value, newCapacity);
}
对于一个StringBuilder对象,我们可以不断的追加字符串到其中,这样就会遇到value数组长度不够的时候,该方法就是用于处理这种情况,在我们实际操作value数组之前,大多会调用该方法判断此次操作之后是否会导致数组溢出,如果是则会将原数组长度扩大两倍加上2并拷贝原数组中的内容到新数组中,然后才实际操作value数组。(此时的value数组已经被扩容了)。
实我们需要始终明白一点,StringBuilder和StringBuffer他们其实和String差不多,内部一样都是封装的字符数组,只不过StringBuilder实现了动态扩容机制,可以动态扩容并且可以动态更改value数组中的元素而已,但本质上都是一样的。
append方法
归根到底核心还是System.arraycopy。
System.arraycopy该方法并没有创建一个新数组,而是对原value数组进行添加移动元素来实现的。
append()方法最后返回的是一个引用。
与String不同的是,因为它的value数组是可变的,所以他的append是在原来value的基础上添加的,并不像String每次修改都是重新新建了一个对象。
String、StringBuffer、StringBuilder的联系与区别
StringBuffer和StringBuilder都继承了抽象类AbstractStringBuilder,这个抽象类和String一样也定义了char[] value和int count,但是与String类不同的是,它们没有final修饰符。因此得出结论:String、StringBuffer和StringBuilder在本质上都是字符数组,不同的是,在进行连接操作时,String每次返回一个新的String实例,而StringBuffer和StringBuilder的append方法直接返回this,所以这就是为什么在进行大量字符串连接运算时,不推荐使用String,而推荐StringBuffer和StringBuilder。那么,哪种情况使用StringBuffe?哪种情况使用StringBuilder呢?
关于StringBuffer和StringBuilder的区别,翻开它们的源码,下面贴出append()方法的实现。
上面第一张图是StringBuffer中append()方法的实现,第二张图为StringBuilder对append()的实现。区别应该一目了然,StringBuffer在方法前加了一个synchronized修饰,起到同步的作用,可以在多线程环境使用。为此付出的代价就是降低了执行效率。因此,如果在多线程环境可以使用StringBuffer进行字符串连接操作,单线程环境使用StringBuilder,它的效率更高。
综上,String是不可变的,StringBuffer和StringBuilder是可变的。