JDK源码分析(8) StringBuffer & StringBuilder
简介
StringBuffer
与StringBuilder
是两个常用的操作字符串的类。大家都知道,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
变量及构造方法
char[] value;
int count;
AbstractStringBuilder() {
}
AbstractStringBuilder(int capacity) {
value = new char[capacity];
}
AbstractStringBuilder
内部用一个char[]
数组保存字符串,可以在构造的时候指定初始容量方法。
扩容
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
值进行拷贝,这显然是耗时操作。
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
的实现。
构造器
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
对象赋初始值。
append()方法
public StringBuilder append(String str) {
super.append(str);
return this;
}
public StringBuilder append(CharSequence s) {
super.append(s);
return this;
}
append()
的重载方法很多,这里随便列举了两个。显然,这里是直接调用的父类AbstractStringBuilder
中的方法。
toString()
public String toString() {
// Create a copy, don't share the array
return new String(value, 0, count);
}
toString()
方法返回了一个新的String
对象,与原来的对象不共享内存。其实AbstractStringBuilder
中的subString()
方法也是如此。
StringBuffer
StiringBuffer
跟StringBuilder
类似,只不过为了实现同步,很多方法使用Synchronized
修饰,如下面的方法:
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,最好预先估计好字符串的大小避免扩容带来的时间消耗。
JDK源码分析(8) StringBuffer & StringBuilder的更多相关文章
- JDK源码分析-String、StringBuilder、StringBuffer
String类的申明 public final class String implements java.io.Serializable, Comparable<String>, Char ...
- JDK源码分析系列---String,StringBuilder,StringBuffer
JDK源码分析系列---String,StringBuilder,StringBuffer 1.String public final class String implements java.io. ...
- JDK源码学习系列03----StringBuffer+StringBuilder
JDK源码学习系列03----StringBuffer+StringBuilder 由于前面学习了StringBuffer和StringBuilder的父类A ...
- JDK源码分析—— ArrayBlockingQueue 和 LinkedBlockingQueue
JDK源码分析—— ArrayBlockingQueue 和 LinkedBlockingQueue 目的:本文通过分析JDK源码来对比ArrayBlockingQueue 和LinkedBlocki ...
- JDK 源码分析(4)—— HashMap/LinkedHashMap/Hashtable
JDK 源码分析(4)-- HashMap/LinkedHashMap/Hashtable HashMap HashMap采用的是哈希算法+链表冲突解决,table的大小永远为2次幂,因为在初始化的时 ...
- JDK源码分析(三)—— LinkedList
参考文档 JDK源码分析(4)之 LinkedList 相关
- JDK源码分析(一)—— String
dir 参考文档 JDK源码分析(1)之 String 相关
- JDK源码分析(2)LinkedList
JDK版本 LinkedList简介 LinkedList 是一个继承于AbstractSequentialList的双向链表.它也可以被当作堆栈.队列或双端队列进行操作. LinkedList 实现 ...
- 【JDK】JDK源码分析-LinkedHashMap
概述 前文「JDK源码分析-HashMap(1)」分析了 HashMap 主要方法的实现原理(其他问题以后分析),本文分析下 LinkedHashMap. 先看一下 LinkedHashMap 的类继 ...
- 【JDK】JDK源码分析-HashMap(1)
概述 HashMap 是 Java 开发中最常用的容器类之一,也是面试的常客.它其实就是前文「数据结构与算法笔记(二)」中「散列表」的实现,处理散列冲突用的是“链表法”,并且在 JDK 1.8 做了优 ...
随机推荐
- Vue-zTree
在vue中引入zTree,和引入其他组件类似,首先在main.js里将以下3个js引入: import "./js/jquery-3.3.1.min.js";import &quo ...
- Spring中关于AOP的实践之Scheme方式实现通知
(刚开始写东西,不足之处还请批评指正) 关于AOP的通知编写方式有两种,使用Schema-baesd或者使用AspectJ方式,本篇主要介绍Schema-baesd方式的代码实现. (注:代码中没有添 ...
- 多线程之Thread
Thread类可以创建和控制线程,Thread类的构造函数重载为接受ThreadStart和ParameterizedThreadStart类型的委托参数. Thread类默认创建的是前台线程,所以我 ...
- mac webstorm无法打开 如何使webstorm不卡
场景:在应用程序里删除了原先的webstorm,然后从官网下载了新的安装包,进行安装.安装后,webstorm就再也打不开了. 解决方案:执行以下命令,清楚webstorm所有缓存,然后重新安装 $ ...
- linux子系统折腾记 (二)
今天一早起床,打开debian,居然出现 错误: 0x80070040 .不知道是怎么回事,网上有篇文章详细介绍了windows linux子系统,打算参考来做做:https://www.jiansh ...
- MongoDB 基础(2019年开篇)
MongoDB基础知识: 1.什么是MongoDB NoSQL(NoSQL=Not Only SQL),意即"不仅仅是SQL". MongoDB是一个介于关系数据库和非关系数据库之 ...
- MyDAL - .Where() & .And() & .Or() 使用
索引: 目录索引 一.API 列表 1.Where .Where(Func<M, bool> func) 如: .Where( it => (it.Prop1>=条件1 &am ...
- hbase rowkey 的设计
什么是rowkey Hbase是一个分布式的.面向列的数据库,它和一般关系型数据库的最大区别是:HBase很适合于存储非结构化的数据,还有就是它基于列的而不是基于行的模式. Hbase是采用K,V存储 ...
- CSS动画总结与呼吸灯效果
首先,先介绍一下主要用到的css属性:animation,text-shadow. text-shadow就不再介绍了,上一篇已经详细介绍了用法.这里先介绍一下animation属性. 1.anima ...
- 关键字-this
1.当成员变量和局部变量重名时,在方法中使用this时,this指向的是该方法所在类的成员变量. package gc.test.java.cs1; public class User{ public ...