Java StringBuilder和StringBuffer源码分析
stringbuilder与stringbuffer是两个常用的操作字符串的类。大家都知道,stringbuilder是线程不安全的,而stringbuffer是线程安全的。前者是jdk1.5加入的,后者在jdk1.0就有了。下面分析一下它们的内部实现。
一、继承关系
public final class stringbuffer extends abstractstringbuilder implements java.io.serializable, charsequence public final class stringbuilder extends abstractstringbuilder implements java.io.serializable, charsequence
可以看到,两个类的继承关系是一模一样的。serializable是可以序列化的标志。charsequence接口包含了charat()、length() 、subsequence()、tostring()这几个方法,string类也实现了这个接口。这里的重点是抽象类abstractstringbuilder,这个类封装了stringbuilder和stringbuffer大部分操作的实现。
二、abstractstringbuilder
1、变量及构造方法
char[] value; int count; abstractstringbuilder() { } abstractstringbuilder(int capacity) { value = new char[capacity]; }
abstractstringbuilder内部用一个char[]数组保存字符串,可以在构造的时候指定初始容量方法。
2、扩容
public void ensurecapacity(int minimumcapacity) { if (minimumcapacity > 0) ensurecapacityinternal(minimumcapacity); } private void ensurecapacityinternal(int minimumcapacity) { // overflow-conscious code if (minimumcapacity - value.length > 0) expandcapacity(minimumcapacity); } 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); }
扩容的方法最终是由expandcapacity()实现的,在这个方法中首先把容量扩大为原来的容量加2,如果此时仍小于指定的容量,那么就把新的容量设为minimumcapacity。然后判断是否溢出,如果溢出了,把容量设为integer.max_value。最后把value值进行拷贝,这显然是耗时操作。
3、append()方法
public abstractstringbuilder append(string str) { if (str == null) return appendnull(); int len = str.length(); ensurecapacityinternal(count + len); str.getchars(0, len, value, count); count += len; return this; }
append()是最常用的方法,它有很多形式的重载。上面是其中一种,用于追加字符串。如果str是null,则会调用appendnull()方法。这个方法其实是追加了'n'、'u'、'l'、'l'这几个字符。如果不是null,则首先扩容,然后调用string的getchars()方法将str追加到value末尾。最后返回对象本身,所以append()可以连续调用。
三、stringbuilder
abstractstringbuilder已经实现了大部分需要的方法,stringbuilder和stringbuffer只需要调用即可。下面来看看stringbuilder的实现。
1、构造器
public stringbuilder() { super(16); } public stringbuilder(int capacity) { super(capacity); } public stringbuilder(string str) { super(str.length() + 16); append(str); } public stringbuilder(charsequence seq) { this(seq.length() + 16); append(seq); }
可以看出,stringbuilder默认的容量大小为16。当然也可以指定初始容量,或者以一个已有的字符序列给stringbuilder对象赋初始值。
2、append()方法
public stringbuilder append(string str) { super.append(str); return this; } public stringbuilder append(charsequence s) { super.append(s); return this; }
append()的重载方法很多,这里随便列举了两个。显然,这里是直接调用的父类abstractstringbuilder中的方法。
3、tostring()
public string tostring() { // create a copy, don't share the array return new string(value, 0, count); }
tostring()方法返回了一个新的string对象,与原来的对象不共享内存。其实abstractstringbuilder中的substring()方法也是如此。
四、sringbuffer
stiringbuffer跟stringbuilder类似,只不过为了实现同步,很多方法使用lsynchronized修饰,如下面的方法:
public synchronized int length() { return count; } public synchronized stringbuffer append(string str) { tostringcache = null; super.append(str); return this; } public synchronized void setlength(int newlength) { tostringcache = null; super.setlength(newlength); }
可以看到,方法前面确实加了synchronized。
另外,在上面的append()以及setlength()方法里面还有个变量tostringcache。这个变量是用于最近一次tostring()方法的缓存,任何时候只要stringbuffer被修改了这个变量会被赋值为null。stringbuffer的tostring如下:
public synchronized string tostring() { if (tostringcache == null) { tostringcache = arrays.copyofrange(value, 0, count); } return new string(tostringcache, true); }
在这个方法中,如果tostringcache为null则先缓存。最终返回的string对象有点不同,这个构造方法还有个参数true。找到string的源码看一下:
string(char[] value, boolean share) { // assert share : "unshared not supported"; this.value = value; }
原来这个构造方法构造出来的string对象并没有实际复制字符串,只是把value指向了构造参数,这是为了节省复制元素的时间。不过这个构造器是具有包访问权限,一般情况下是不能调用的。
总结
- stringbuilder和stringbuffer都是可变字符串,前者线程不安全,后者线程安全。
- stringbuilder和stringbuffer的大部分方法均调用父类abstractstringbuilder的实现。其扩容机制首先是把容量变为原来容量的2倍加2。最大容量是integer.max_value,也就是0x7fffffff。
- stringbuilder和stringbuffer的默认容量都是16,最好预先估计好字符串的大小避免扩容带来的时间消耗。
以上就是本文的全部内容,希望对大家学习java中两个常用的操作字符串的类stringbuilder和stringbuffer有所帮助。
上一篇: php 开发中加密的几种方法总结
推荐阅读
-
MyBatis 源码分析 之SqlSession接口和Executor类
-
java 同步、异步、阻塞和非阻塞分析
-
java编程之单元测试(Junit)实例分析(附实例源码)
-
Java编程之jdk1.4,jdk1.5和jdk1.6的区别分析(经典)
-
Java String源码分析并介绍Sting 为什么不可变
-
Java 中String StringBuilder 与 StringBuffer详解及用法实例
-
从JVM分析Java的类的加载和卸载机制
-
java 中modCount 详解及源码分析
-
MyBatis 源码分析 之SqlSession接口和Executor类
-
流程图+源码深入分析:缓存穿透和击穿问题出现原理以及可落地的解决方案