AbstractStringBuilder是一个抽象类,它的两个子类为StringBuffer和StringBuilder。它实现的接口CharSequence和Appendable。
实现的接口
- CharSequence:在String类源码分析中已经解释。
- Appendable接口:该接口里面只有三个方法,三个方法只有参数不相同,方法名都是一样的。
Appendable append(char c):将指定的字符附加到此 Appendable 。
Appendable append(CharSequence csq):将指定的字符序列追加到此 Appendable 。
Appendable append(CharSequence csq, int start, int end):将指定的字符序列的子序列附加到此 Appendable 。
成员变量
char[] value;
int count;
这两个成员变量一个是用来存储字符,一个是用来记录存储了多少个字符,String类也是用一个char数组存储字符。
构造方法
/**
* This no-arg constructor is necessary for serialization of subclasses.
*/
AbstractStringBuilder() {
}
/**
* Creates an AbstractStringBuilder of the specified capacity.
*/
AbstractStringBuilder(int capacity) {
value = new char[capacity];
}
该抽象类有两个构造方法,一个午餐构造方法和一个有参构造方法,有参构造方法传了一个int类型的参数,初始化成员变量value的容量。后续有个方法有关系。
具体方法
(1)
@Override
public int length() {
return count;
}
/**
* Returns the current capacity. The capacity is the amount of storage
* available for newly inserted characters, beyond which an allocation
* will occur.
*
* @return the current capacity
*/
public int capacity() {
return value.length;
}
这两个方法,第一个是返回记录存储了多少个字符,第二个是返回成员变量value的容量(因为构造方法可以初始化改成员变量value的容量大小,所以用这两个方法区别开来)
(2)
public void ensureCapacity(int minimumCapacity) {
if (minimumCapacity > 0)
ensureCapacityInternal(minimumCapacity);
}
/**
* This method has the same contract as ensureCapacity, but is
* never synchronized.
*/
private void ensureCapacityInternal(int minimumCapacity) {
// overflow-conscious code
if (minimumCapacity - value.length > 0)
expandCapacity(minimumCapacity);
}
/**
* This implements the expansion semantics of ensureCapacity with no
* size check or synchronization.
*/
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);
}
/**
* Attempts to reduce storage used for the character sequence.
* If the buffer is larger than necessary to hold its current sequence of
* characters, then it may be resized to become more space efficient.
* Calling this method may, but is not required to, affect the value
* returned by a subsequent call to the {@link #capacity()} method.
*/
public void trimToSize() {
if (count < value.length) {
value = Arrays.copyOf(value, count);
}
}
public void setLength(int newLength) {
if (newLength < 0)
throw new StringIndexOutOfBoundsException(newLength);
ensureCapacityInternal(newLength);
if (count < newLength) {
Arrays.fill(value, count, newLength, '\0');
}
count = newLength;
}
上面五个方法都跟缓冲区有关。
ensureCapacity(int minimumCapacity):确保容量至少等于指定的最小值。如果当前容量小于参数,则分配一个新的具有更大容量的内部数组。他的具体实现调用的下面的方法ensureCapacityInternal(int minimumCapacity)。这两个方法总体来说,首先传递一个int类型minimumCapacity,判断该参数是否大于0,大于零调用ensureCapacityInternal(int minimumCapacity),该方法用
minimumCapacity去与value.length相减,如果大于零则数组扩容minimumCapacity大小,否则取原value大小。
expandCapacity(int minimumCapacity):该方法也是扩容,但不需要检查minimumCapacity是否大于或者小于0,它的实现首先初始化一个大小为value.length * 2 + 2的值,然后与minimumCapacity相减,如果小于0说明minimumCapacity大于value.length * 2 + 2,容量值大小取minimumCapacity,否则取value.length * 2 + 2,最后如果这两个值都小于0,则初始化容量大小为Integer.MAX_VALUE。
trimToSize():试图减少用于字符序列的存储。如果缓冲区大于容纳当前字符序列所需的大小,则可以调整大小以提高空间效率。调用此方法可能(但不是必需的)影响后续调用capacity()方法返回的值。
setLength(int newLength):设置字符序列的长度。将序列更改为一个新的字符序列,其长度由参数指定。对于每一个小于newLength的非负索引k,如果k小于旧字符序列的长度,则新字符序列中索引k处的字符与旧字符序列中索引k处的字符相同;否则,它就是空字符’\u0000’。换句话说,如果newLength参数小于当前长度,则将该长度更改为指定的长度。
如果newLength参数大于或等于当前l(有道翻译)
从实现可以看出当newLength<0时,抛出一个StringIndexOutOfBoundsException(newLength),然后调用ensureCapacityInternal(newLength)(不管newLength>0或者小于0都会调用),因为newLength<0所有取原来数组。如果
count < newLength调用的 Arrays.fill(value, count, newLength, ‘\0’);
public static void fill(char[] a, int fromIndex, int toIndex, char val) {
rangeCheck(a.length, fromIndex, toIndex);
for (int i = fromIndex; i < toIndex; i++)
a[i] = val;
}
该方法是从a下标索引fromIndex开始到toIndex下标的字符替换成val
(3)
public AbstractStringBuilder append(String str):该方法先判断str是否为null,如果为null,则在调用该方法对象中加入null字符数组。如果不为空,先初始化该对象的容量,利用String类里面的getChars方法把str加到该对象后面。其余的是重载的参数不同。
public AbstractStringBuilder append(Object obj) {
return append(String.valueOf(obj));
}
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;
}
public AbstractStringBuilder append(StringBuffer sb) {
if (sb == null)
return appendNull();
int len = sb.length();
ensureCapacityInternal(count + len);
sb.getChars(0, len, value, count);
count += len;
return this;
}
AbstractStringBuilder append(AbstractStringBuilder asb) {
if (asb == null)
return appendNull();
int len = asb.length();
ensureCapacityInternal(count + len);
asb.getChars(0, len, value, count);
count += len;
return this;
}
private AbstractStringBuilder appendNull() {
int c = count;
ensureCapacityInternal(c + 4);
final char[] value = this.value;
value[c++] = 'n';
value[c++] = 'u';
value[c++] = 'l';
value[c++] = 'l';
count = c;
return this;
}
public AbstractStringBuilder append(CharSequence s):该方法重写Appendable里面的方法。看源码也不是很难理解。
注意:在该类中凡是改变了value的值,count也要随着变化。
@Override
public AbstractStringBuilder append(CharSequence s) {
if (s == null)
return appendNull();
if (s instanceof String)
return this.append((String)s);
if (s instanceof AbstractStringBuilder)
return this.append((AbstractStringBuilder)s);
return this.append(s, 0, s.length());
}
@Override
public AbstractStringBuilder append(CharSequence s, int start, int end) {
if (s == null)
s = "null";
if ((start < 0) || (start > end) || (end > s.length()))
throw new IndexOutOfBoundsException(
"start " + start + ", end " + end + ", s.length() "
+ s.length());
int len = end - start;
ensureCapacityInternal(count + len);
for (int i = start, j = count; i < end; i++, j++)
value[j] = s.charAt(i);
count += len;
return this;
}
ps:关于instanceof:
子类 instanceof 父类 == true
父类 instanceof 子类 == false
其余的append(param)实现的方法都差不多。 public static native void arraycopy(Object src, int srcPos,Object dest, int destPos,int length):这个方法真的很好用,源码里面这个方法出现的次数很多,对数组进行缩容和扩容很好用。
(4)
public AbstractStringBuilder delete(int start, int end) {
if (start < 0)
throw new StringIndexOutOfBoundsException(start);
if (end > count)
end = count;
if (start > end)
throw new StringIndexOutOfBoundsException();
int len = end - start;
if (len > 0) {
System.arraycopy(value, start+len, value, start, count-end);
count -= len;
}
return this;
}
public AbstractStringBuilder deleteCharAt(int index) {
if ((index < 0) || (index >= count))
throw new StringIndexOutOfBoundsException(index);
System.arraycopy(value, index+1, value, index, count-index-1);
count--;
return this;
}
delete(int start, int end):删除此序列子字符串中的字符。子字符串从指定的开始处开始,并扩展到索引末尾- 1处的字符,如果不存在这样的字符,则扩展到序列末尾。如果开始等于结束,则不做任何更改。
都是利用System.arraycopy方法进行缩容
(5)
public AbstractStringBuilder replace(int start, int end, String str) {
if (start < 0)
throw new StringIndexOutOfBoundsException(start);
if (start > count)
throw new StringIndexOutOfBoundsException("start > length()");
if (start > end)
throw new StringIndexOutOfBoundsException("start > end");
if (end > count)
end = count;
int len = str.length();
int newCount = count + len - (end - start);
ensureCapacityInternal(newCount);
System.arraycopy(value, end, value, start + len, count - end);
str.getChars(value, start);
count = newCount;
return this;
}
public AbstractStringBuilder replace(int start, int end, String str):用指定字符串中的字符替换此序列子字符串中的字符。子字符串从指定的开始处开始,并扩展到索引末尾- 1处的字符,如果不存在这样的字符,则扩展到序列末尾。首先删除子字符串中的字符,然后在开始处插入指定的字符串。(如果需要,这个序列将被加长以适应指定的字符串。)
该方法实现也是利用System.arraycopy方法进行数组操作,最后在把给的字符串参数加到指定位置。
(5)
public AbstractStringBuilder insert(int dstOffset, CharSequence s,
int start, int end) {
if (s == null)
s = "null";
if ((dstOffset < 0) || (dstOffset > this.length()))
throw new IndexOutOfBoundsException("dstOffset "+dstOffset);
if ((start < 0) || (end < 0) || (start > end) || (end > s.length()))
throw new IndexOutOfBoundsException(
"start " + start + ", end " + end + ", s.length() "
+ s.length());
int len = end - start;
ensureCapacityInternal(count + len);
System.arraycopy(value, dstOffset, value, dstOffset + len,
count - dstOffset);
for (int i=start; i<end; i++)
value[dstOffset++] = s.charAt(i);
count += len;
return this;
}
public AbstractStringBuilder insert(int dstOffset, CharSequence s,int start, int end):将指定字符序列的子序列插入到此序列中。按顺序,将start和end指定的参数s的子序列插入到指定目标偏移量处的序列中,向上移动最初位于该位置之上的任何字符。这个序列的长度由结束-开始递增
从实现源码来看,首先进行参数校验,然后在求出复制后数组的大小,最后替换指定范围类为替换的字符串。
改方法有很多重载方法,无非是转换成调用这个所要的参数来实现的。
(6)
public AbstractStringBuilder reverse() {
boolean hasSurrogates = false;
int n = count - 1;
for (int j = (n-1) >> 1; j >= 0; j--) {
int k = n - j;
char cj = value[j];
char ck = value[k];
value[j] = ck;
value[k] = cj;
if (Character.isSurrogate(cj) ||
Character.isSurrogate(ck)) {
hasSurrogates = true;
}
}
if (hasSurrogates) {
reverseAllValidSurrogatePairs();
}
return this;
}
/** Outlined helper method for reverse() */
private void reverseAllValidSurrogatePairs() {
for (int i = 0; i < count - 1; i++) {
char c2 = value[i];
if (Character.isLowSurrogate(c2)) {
char c1 = value[i + 1];
if (Character.isHighSurrogate(c1)) {
value[i++] = c1;
value[i] = c2;
}
}
}
}
public AbstractStringBuilder reverse() :将此字符序列替换为该序列的反面。如果序列中包含任何代理对,则将这些代理对视为反向操作的单个字符。因此,高-低代位体的顺序永远不会颠倒。设n是这个字符序列的字符长度(不是char值中的长度),刚好在执行反向方法之前。那么新字符序列中下标k处的字符就等于旧字符序列中下标n-k-1处的字符。
注意,反向操作可能导致生成代理
Character源码没有阅读,以后再反过来补充下。
总结:该抽象类主要有两个实现类,提供了一下操作字符数组的方法:append,insert,charAt,subString,reverse,indexOf等方法。要注意的是该类的扩容机制。并且该类权限默认friendly 。