系列文章:
- JDK源码分析-transient关键字
- JDK源码分析-transient关键字
- JDK源码分析-String
- JDK源码分析-AbstractStringBuilder
- JDK源码分析-StringBuffer和StringBuilder
- JDK源码分析-RandomAccess
- JDK源码分析-ArrayList
- JDK源码分析-LinkedList
- JDK源码分析-HashMap
- JDK源码分析-HashSet
- JDK源码分析-LinkedHashMap
- JDK源码分析-ConcurrentHashMap
类申明
abstract class AbstractStringBuilder implements Appendable, CharSequence
- AbstractStringBuilder类名用abstract修饰说明是一个抽象类,只能被继承,不能直接创建对象
- 实现了Appendable接口,Appendable能够被追加 char 序列和值的对象。如果某个类的实例打算接收来自 Formatter 的格式化输出,那么该类必须实现 Appendable 接口
- 实现了Charsequence接口,代表该类,或其子类是一个字符序列
成员变量
/**
* 内部是一个char[] value,不再是final的了,也就意味着可变
* 这个char[] value 是核心
*/
char[] value;
/**
* 记录已经使用的字符个数
*/
int count;
核心方法
append方法
append方法提供了15个重载的方法
- append(Object obj)
- append(String str)
- append(StringBuffer sb)
- append(AbstractStringBuilder asb)
- append(CharSequence s)
- append(CharSequence s, int start, int end)
- append(char[] str)
- append(char str[], int offset, int len)
- append(boolean b) 以字符串'true'或者'false'内容插入
- append(char c)
- append(int i)
- append(long l)
- append(float f)
- append(double d)
- appendNull()比较特殊,下面会有讲到
其调用Sequence图如下
以append(String str)代码为例,
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;
}
代码中第一步是检查参数str是否为空,当为空的时候,直接在原有的AbstractStringBuilder后面追加上null字符串
private AbstractStringBuilder appendNull() {
int c = count;
//追加null字符串,并把原有长度扩大4
ensureCapacityInternal(c + 4);
final char[] value = this.value;
value[c++] = 'n';
value[c++] = 'u';
value[c++] = 'l';
value[c++] = 'l';
count = c;
return this;
}
char数组的长度在使用前是要初始化的,所有每次append新值的时候都会检查这个内部数组的长度,以防止数组越界
//检查数据越界的核心代码
private void ensureCapacityInternal(int minimumCapacity) {
// overflow-conscious code
if (minimumCapacity - value.length > 0) {
value = Arrays.copyOf(value,
newCapacity(minimumCapacity));
}
}
private int newCapacity(int minCapacity) {
// overflow-conscious code
int newCapacity = (value.length << 1) + 2;
if (newCapacity - minCapacity < 0) {
newCapacity = minCapacity;
}
return (newCapacity <= 0 || MAX_ARRAY_SIZE - newCapacity < 0)
? hugeCapacity(minCapacity)
: newCapacity;
}
private int hugeCapacity(int minCapacity) {
if (Integer.MAX_VALUE - minCapacity < 0) { // 超过最大长度
throw new OutOfMemoryError();
}
return (minCapacity > MAX_ARRAY_SIZE)
? minCapacity : MAX_ARRAY_SIZE;
}
当append(String str)其中str不为空的时候,把str字符串append到原有的AbstractStringBuilder的方法是调用了String的getChars方法
public void getChars(int srcBegin, int srcEnd, char dst[], int dstBegin) {
if (srcBegin < 0) {
throw new StringIndexOutOfBoundsException(srcBegin);
}
if (srcEnd > value.length) {
throw new StringIndexOutOfBoundsException(srcEnd);
}
if (srcBegin > srcEnd) {
throw new StringIndexOutOfBoundsException(srcEnd - srcBegin);
}
//native方法
System.arraycopy(value, srcBegin, dst, dstBegin, srcEnd - srcBegin);
}
System.arraycopy是一个native方法,可以使用这个方法来实现数组之间的复制。对于一维数组来说,这种复制属性值传递,修改副本不会影响原来的值。对于二维或者一维数组中存放的是对象时,复制结果是一维的引用变量传递给副本的一维数组,修改副本时,会影响原来的数组。
package com.tutorialspoint;
import java.lang.*;
public class SystemDemo {
public static void main(String[] args) {
int arr1[] = { 0, 1, 2, 3, 4, 5 };
int arr2[] = { 5, 10, 20, 30, 40, 50 };
// 把arr1中的从0位置开的的数组,copy到arr2中起始位置为0长度为1的部分
System.arraycopy(arr1, 0, arr2, 0, 1);
System.out.print("array2 = ");
System.out.print(arr2[0] + " ");
System.out.print(arr2[1] + " ");
System.out.print(arr2[2] + " ");
System.out.print(arr2[3] + " ");
System.out.print(arr2[4] + " ");
}
}
//输出
array2 = 0 10 20 30 40
具体用法参考:Java.lang.System.arraycopy() Method
insert方法
提供了12个重载的insert方法
- insert(int index, char[] str, int offset,int len)
- insert(int offset, Object obj)
- insert(int offset, String str)
- insert(int offset, char[] str)
- insert(int dstOffset, CharSequence s)
- insert(int dstOffset, CharSequence s,int start, int end)
- insert(int offset, boolean b)
- insert(int offset, char c)
- insert(int offset, int i)
- insert(int offset, long l)
- insert(int offset, float f)
- insert(int offset, double d)
其中最核心的方法是insert(int offset, String str),其他方法都是通过String.valueOf()来调用这个方法的。
而insert的逻辑和append逻辑大致相同,不同点在于多了指定位置的检查和插入。
public AbstractStringBuilder insert(int offset, String str) {
//检查插入的位置是否合法
if ((offset < 0) || (offset > length()))
throw new StringIndexOutOfBoundsException(offset);
//null字符串的特殊处理
if (str == null)
str = "null";
int len = str.length();
ensureCapacityInternal(count + len);
System.arraycopy(value, offset, value, offset + len, count - offset);
str.getChars(value, offset);
count += len;
return this;
}
reverse方法
AbstractStringBuilder提供了一个reverse方法,可以翻转字符序列。
java采用UTF-16编码unicode,UTF-16使用使用一个6位单元或者两个16为单元表示一个unicode字符。使用两个单元的,前面那个单元叫highsurrogate 后面那个叫lowsurrogate
public AbstractStringBuilder reverse() {
//用于判断字符序列中是否包含surrogates pair,即表示unicode编码占用一个还是两个字符
boolean hasSurrogates = false;
int n = count - 1;
//翻转算法是把整个char[]换分为前后,从中间到两头依次交换,(n-1) >> 1相当于(n-1)/2取得中轴
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;
//当一个字符是Surrogate时,特殊标记
if (Character.isSurrogate(cj) ||
Character.isSurrogate(ck)) {
hasSurrogates = true;
}
}
//如果序列中包含surrogates pair 则执行reverseAllValidSurrogatePairs
if (hasSurrogates) {
reverseAllValidSurrogatePairs();
}
return this;
}
//特殊处理Surrogate,即highsurrogate和lowsurrogate作为一对,把序列调整过来
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;
}
}
}
}
补充一下:
StringBuffer stringBuffer = new StringBuffer("我在上海🌉");
System.out.println(stringBuffer.reverse());
System.out.println(stringBuffer.length());
输出:
🌉海上在我
6
从打印结果来看,整个字符串的长度是6位,而不是5位,其中🌉占了2位 🌉在java中的编码是"\uD83C\uDF09" , "\uD83C“就是lowsurrogate,而"\uDF09"是highsurrogate lowsurrogate和highsurrogate需要作为一个整体来翻转,而不能拆开
参考: