diff --git a/szgz-branch1/代码注释.txt b/szgz-branch1/代码注释.txt new file mode 100644 index 0000000..083c0f8 --- /dev/null +++ b/szgz-branch1/代码注释.txt @@ -0,0 +1,1245 @@ +// AbstractMethodError类继承自IncompatibleClassChangeError,表示在类的继承体系中出现了抽象方法相关的不兼容变更错误 +public class AbstractMethodError extends IncompatibleClassChangeError { + // 序列化版本号,用于在Java对象序列化和反序列化过程中确保版本的兼容性 + // 当一个类实现了Serializable接口,这个静态常量用于标识类的版本信息 + // 如果类的结构(比如成员变量、方法等有变更),合适地更新这个版本号有助于避免序列化和反序列化出现不兼容问题 + @java.io.Serial + private static final long serialVersionUID = -1654391082989018462L; +} +// AbstractMethodError类的默认构造函数,用于创建AbstractMethodError类的实例 +// 它通过调用父类(IncompatibleClassChangeError)的默认构造函数(super())来完成初始化相关操作 +// 这样在创建AbstractMethodError类的实例时,能够确保继承体系中父类部分的初始化按预期进行 +public AbstractMethodError() { + super(); +} +// AbstractStringBuilder类的构造函数,其作用是基于给定的字符串来初始化AbstractStringBuilder对象 +// 参数str为用于初始化的字符串 +AbstractStringBuilder(String str) { + // 获取传入字符串的长度,后续会基于这个长度来决定一些内部存储相关的容量设置等操作 + int length = str.length(); + + // 根据传入字符串的长度来计算合适的初始容量值(capacity) + // 如果字符串长度小于整型最大值减去16,那么容量设置为字符串长度加上16,这么做通常是为了预留一定的额外空间,方便后续可能的字符串拼接等操作 + // 如果字符串长度大于等于整型最大值减去16,那就将容量设置为整型最大值,避免出现溢出等异常情况 + int capacity = (length < Integer.MAX_VALUE - 16) + ? length + 16 : Integer.MAX_VALUE; + + // 获取传入字符串的编码格式(coder),在Java中字符串有不同的编码表示方式,这里先获取其初始编码情况 + final byte initCoder = str.coder(); + // 将当前AbstractStringBuilder对象的编码格式设置为和传入字符串相同的编码格式 + coder = initCoder; + + // 根据编码格式来初始化内部用于存储字符串内容的字节数组(value) + // 如果编码格式是LATIN1(一种相对简单紧凑的单字节编码,常用于ASCII字符等情况),那就创建一个容量为刚才计算所得的capacity大小的字节数组 + // 如果编码格式不是LATIN1,就通过StringUTF16类的相关方法来创建合适的字节数组用于存储,这个字节数组是针对UTF-16编码格式的存储而创建的 + value = (initCoder == LATIN1) + ? new byte[capacity] : StringUTF16.newBytesFor(capacity); + + // 调用append方法将传入的字符串内容添加到当前AbstractStringBuilder对象中,完成整个初始化过程基于已有字符串内容的填充操作 + append(str); +} +// compareTo方法用于比较当前AbstractStringBuilder对象与另一个AbstractStringBuilder对象的大小关系 +// 根据字符串内容按照字典序等规则来确定返回值,返回值小于0表示当前对象小于传入的另一个对象,等于0表示二者相等,大于0表示当前对象大于另一个对象 +int compareTo(AbstractStringBuilder another) { + // 首先判断当前对象与传入的另一个对象是否是同一个实例(通过内存地址比较) + // 如果是同一个对象,那它们自然是相等的,直接返回0 + if (this == another) { + return 0; + } + + // 获取当前AbstractStringBuilder对象内部存储字符串内容的字节数组,方便后续比较操作使用 + byte[] val1 = value; + // 获取传入的另一个AbstractStringBuilder对象内部存储字符串内容的字节数组 + byte[] val2 = another.value; + // 获取当前AbstractStringBuilder对象中已存储的字符数量(有效字符数量) + int count1 = this.count; + // 获取传入的另一个AbstractStringBuilder对象中已存储的字符数量 + int count2 = another.count; + + // 判断当前对象与另一个对象的编码格式(coder)是否相同 + if (coder == another.coder) { + // 如果编码格式相同,进一步判断当前对象是否采用的是LATIN1编码(通过调用isLatin1方法来判断,一般LATIN1编码常用于处理ASCII字符等较简单的情况) + return isLatin1()? + // 如果是LATIN1编码,调用StringLatin1类的compareTo方法来比较两个字节数组(对应两个对象存储的内容),传入各自的字节数组以及有效字符数量等参数进行比较 + StringLatin1.compareTo(val1, val2, count1, count2) + // 如果不是LATIN1编码,那通常就是UTF16编码(在常见的Java字符串处理场景下),调用StringUTF16类的compareTo方法来进行比较操作,同样传入相关参数 + : StringUTF16.compareTo(val1, val2, count1, count2); + } + // 如果当前对象与另一个对象的编码格式不同 + return isLatin1()? + // 如果当前对象是LATIN1编码,调用StringLatin1类的compareToUTF16方法来比较当前对象(LATIN1编码)与另一个对象(非LATIN1编码,大概率是UTF16编码)的内容,传入相应参数进行比较 + StringLatin1.compareToUTF16(val1, val2, count1, count2) + // 如果当前对象不是LATIN1编码,那调用StringUTF16类的compareToLatin1方法来比较当前对象(大概率是UTF16编码)与另一个对象(LATIN1编码)的内容,同样传入对应参数来执行比较操作 + : StringUTF16.compareToLatin1(val1, val2, count1, count2); +} +// @Override注解表示下面这个方法重写了父类(或实现的接口中定义的)同名方法,用于确保重写的正确性以及增强代码的可读性和可维护性 +// length方法用于返回当前对象中所包含的字符数量,在这个类的实现里,直接返回count变量的值,说明count变量记录着有效的字符数量 +@Override +public int length() { + return count; +} + +// capacity方法用于返回当前对象内部用于存储字符内容的容量大小 +// 通过对内部存储字符的字节数组(value)的长度进行一定运算来获取容量信息 +// 具体是将字节数组的长度右移coder位(这里的右移操作结合coder的值,可能与编码格式相关,不同编码格式下字节数组存储字符的方式和密度不同,通过这种位移运算来准确获取对应的容量情况) +public int capacity() { + return value.length >> coder; +} +// ensureCapacity方法用于确保当前对象内部存储字符的容量能够满足给定的最小容量需求(minimumCapacity) +// 如果传入的最小容量大于0,说明有容量扩充的潜在需求,此时调用ensureCapacityInternal方法进一步处理容量确保的相关操作 +public void ensureCapacity(int minimumCapacity) { + if (minimumCapacity > 0) { + ensureCapacityInternal(minimumCapacity); + } +} + +// ensureCapacityInternal方法是实际处理确保容量的内部方法,主要负责判断当前容量是否满足需求,并在不满足时进行容量扩充操作 +// 这里的代码在处理容量相关运算时考虑了整型溢出的情况,以保证程序的健壮性 +private void ensureCapacityInternal(int minimumCapacity) { + // overflow-conscious code:表示下面这段代码在进行容量相关计算时考虑了可能出现的整型溢出问题,避免因数值过大导致异常情况 + + // 获取当前对象内部存储字符的字节数组(value)基于编码格式(coder)转换后的现有容量大小(通过右移操作获取,不同编码下字节数组长度与实际可容纳字符数量的换算关系不同) + int oldCapacity = value.length >> coder; + // 判断传入的最小容量需求是否大于现有的容量,如果大于,说明当前容量不足,需要进行扩充操作 + if (minimumCapacity - oldCapacity > 0) { + // 通过调用Arrays.copyOf方法来扩充字节数组的大小,传入原字节数组(value)以及新的容量大小(通过newCapacity方法获取并结合编码格式进行左移操作转换后的长度) + // 这样可以创建一个新的、具有更大容量的字节数组,并将原数组内容复制过去,完成容量扩充 + value = Arrays.copyOf(value, + newCapacity(minimumCapacity) << coder); + } +} + +// newCapacity方法用于计算新的合适的容量大小,综合考虑了当前的容量情况、需要扩充的容量(growth)以及一些预设的容量增长规则等因素 +private int newCapacity(int minCapacity) { + // 获取当前内部存储字符的字节数组(value)的长度,这是现有字节数组的实际长度,后续会基于此来计算新的长度 + int oldLength = value.length; + // 根据传入的最小容量需求(minCapacity)以及编码格式(coder)来计算出期望达到的新的字节数组长度,通过左移操作来进行基于编码格式的长度换算(不同编码下长度换算方式不同) + int newLength = minCapacity << coder; + // 计算出需要增长的容量大小,即期望的新长度与现有长度的差值,这个差值将用于后续进一步判断容量增长的幅度等情况 + int growth = newLength - oldLength; + // 通过调用ArraysSupport类的newLength方法来确定最终的新长度,传入现有长度、需要增长的容量以及一个基于现有长度和固定增量(2 << coder,这里的2结合编码格式左移后的数值可能是一种预设的容量增长基本幅度)计算出的参考长度值 + // ArraysSupport.newLength方法内部可能有一套自己的容量增长策略,会综合考虑这些参数来返回一个合适的新长度值,避免过度或不足的容量扩充 + int length = ArraysSupport.newLength(oldLength, growth, oldLength + (2 << coder)); + // 如果计算出的新长度等于整型最大值(Integer.MAX_VALUE),说明容量已经达到了系统所允许的最大极限,此时抛出OutOfMemoryError异常,并在异常信息中说明所需长度超出了实现的限制 + if (length == Integer.MAX_VALUE) { + throw new OutOfMemoryError("Required length exceeds implementation limit"); + } + // 将最终确定的新长度按照编码格式进行右移操作转换,得到对应实际可容纳字符数量的容量值,并返回该容量值,用于前面方法中扩充字节数组等操作的参数传递 + return length >> coder; +} +// inflate方法用于将当前对象内部存储的字符串内容从可能的LATIN1编码格式转换为UTF16编码格式(通常是一种更通用、能处理更多字符的编码格式) +// 如果当前对象存储的内容不是LATIN1编码格式,那就直接返回,不进行任何操作 +private void inflate() { + if (!isLatin1()) { + return; + } + // 如果是LATIN1编码格式,先创建一个新的字节数组buf,其大小根据当前存储内容的字节数组(value)的长度来确定,这个新字节数组将用于存储转换为UTF16编码后的内容 + byte[] buf = StringUTF16.newBytesFor(value.length); + // 调用StringLatin1类的inflate方法,将当前LATIN1编码格式的字节数组(value)中的内容转换并填充到新创建的UTF16编码格式对应的字节数组(buf)中 + // 参数依次表示源字节数组(value)、源字节数组起始位置(0)、目标字节数组(buf)、目标字节数组起始位置(0)以及要转换的有效字符数量(count) + StringLatin1.inflate(value, 0, buf, 0, count); + // 将当前对象内部存储字符串内容的字节数组替换为新的、已经转换为UTF16编码格式的字节数组buf + this.value = buf; + // 将当前对象的编码格式(coder)设置为UTF16,以反映内部存储内容编码格式的变更 + this.coder = UTF16; +} + +// trimToSize方法用于将当前对象内部存储字符串内容的字节数组调整为恰好能容纳现有字符数量的大小,避免浪费多余的内存空间 +public void trimToSize() { + // 根据当前对象中有效字符的数量(count)以及编码格式(coder)来计算出实际需要的字节数组长度,通过左移操作来进行基于编码格式的长度换算(不同编码下字节数组长度与实际可容纳字符数量的对应关系不同) + int length = count << coder; + // 判断当前字节数组的实际长度是否大于计算出的刚好容纳现有字符数量所需的长度,如果大于,说明存在多余空间,需要进行数组收缩操作 + if (length < value.length) { + // 通过调用Arrays.copyOf方法创建一个新的字节数组,其长度为刚好容纳现有字符数量所需的长度(length),并将原字节数组(value)中的内容复制到新数组中,实现对原字节数组的收缩,节省内存空间 + value = Arrays.copyOf(value, length); + } +} + +// setLength方法用于设置当前对象中字符串内容的长度,也就是有效字符的数量 +// 可以用于截断或扩展字符串内容的长度,但会进行一些合法性校验以及相应的字符填充或其他相关处理 +public void setLength(int newLength) { + // 首先对传入的新长度(newLength)进行合法性校验,如果新长度小于0,说明不符合要求,抛出StringIndexOutOfBoundsException异常,并指明异常是由传入的非法长度值导致的 + if (newLength < 0) { + throw new StringIndexOutOfBoundsException(newLength); + } + // 调用ensureCapacityInternal方法确保内部存储字节数组有足够的容量来容纳新长度对应的字符内容,避免后续操作出现数组越界等问题 + ensureCapacityInternal(newLength); + // 如果当前对象中已有的有效字符数量(count)小于新长度(newLength),说明需要对字符串内容进行扩展填充操作 + if (count < newLength) { + // 判断当前对象存储内容是否采用的是LATIN1编码格式,如果是,调用StringLatin1类的fillNull方法来填充字节数组中剩余的空间,使其达到新长度对应的字符数量要求 + if (isLatin1()) { + StringLatin1.fillNull(value, count, newLength); + } + // 如果不是LATIN1编码格式(大概率是UTF16编码格式),则调用StringUTF16类的fillNull方法来进行相应的填充操作 + else { + StringUTF16.fillNull(value, count, newLength); + } + } + // 如果当前对象中已有的有效字符数量(count)大于新长度(newLength),说明需要对字符串进行截断操作,这里可能将maybeLatin1标志位设置为true(具体含义与字符串是否可能采用LATIN1编码相关,可能是一种优化相关的判断标志) + else if (count > newLength) { + maybeLatin1 = true; + } + // 最后将当前对象中有效字符的数量更新为传入的新长度(newLength),完成字符串长度的设置操作 + count = newLength; +} + +// charAt方法用于获取当前对象中指定索引位置处的字符 +// 会先进行索引的合法性校验,然后根据当前对象内部存储内容的编码格式来采用不同的方式获取字符 +@Override +public char charAt(int index) { + // 调用checkIndex方法对传入的索引(index)进行合法性校验,确保索引在有效字符数量范围(count)内,避免出现越界访问等异常情况 + checkIndex(index, count); + // 判断当前对象存储内容是否采用的是LATIN1编码格式,如果是,通过将字节数组(value)中对应索引位置的字节转换为字符(通过与0xff进行按位与操作,将字节值转换为无符号的字符表示形式)来获取指定位置的字符 + if (isLatin1()) { + return (char)(value[index] & 0xff); + } + // 如果不是LATIN1编码格式(大概率是UTF16编码格式),则调用StringUTF16类的getChar方法来获取指定索引位置处的字符,该方法内部会根据UTF16编码的规则进行相应的字符提取操作 + return StringUTF16.getChar(value, index); +} +// codePointAt方法用于获取指定索引位置处的代码点(Code Point,在Unicode中,一个代码点可以对应一个字符,对于一些复杂字符可能由多个代码单元组成,而UTF-16编码下一个代码单元通常是两个字节) +// 参数index表示要获取代码点的字符索引位置 +public int codePointAt(int index) { + // 获取当前对象中已存储的有效字符数量,方便后续进行边界检查等操作 + int count = this.count; + // 获取当前对象内部存储字符串内容的字节数组,后续根据编码格式及索引来提取相应的代码点信息 + byte[] value = this.value; + // 调用checkIndex方法对传入的索引(index)进行合法性校验,确保索引在有效字符数量范围(count)内,避免出现越界访问等异常情况 + checkIndex(index, count); + // 判断当前对象存储内容是否采用的是LATIN1编码格式,如果是,通过将字节数组(value)中对应索引位置的字节转换为无符号的整数值(通过与0xff进行按位与操作)来获取该位置对应的代码点(在LATIN1编码下,一个字节通常就代表一个字符对应的代码点) + if (isLatin1()) { + return value[index] & 0xff; + } + // 如果不是LATIN1编码格式(大概率是UTF-16编码格式),则调用StringUTF16类的codePointAtSB方法来获取指定索引位置处的代码点,该方法内部会根据UTF-16编码以及字符串构建器的相关特性进行相应的代码点提取操作 + return StringUTF16.codePointAtSB(value, index, count); +} + +// codePointBefore方法用于获取指定索引位置前一个位置处的代码点 +// 参数index表示参考的索引位置,获取的是该位置前一个字符对应的代码点 +public int codePointBefore(int index) { + // 先将传入的索引减1,得到要获取代码点的目标索引位置(因为是获取前一个位置处的代码点) + int i = index - 1; + // 调用checkIndex方法对计算得到的索引(i)进行合法性校验,确保其在有效字符数量范围(count)内,避免出现越界访问等异常情况 + checkIndex(i, count); + // 判断当前对象存储内容是否采用的是LATIN1编码格式,如果是,通过将字节数组(value)中对应索引位置的字节转换为无符号的整数值(通过与0xff进行按位与操作)来获取该位置对应的代码点(在LATIN1编码下,一个字节通常就代表一个字符对应的代码点) + if (isLatin1()) { + return value[i] & 0xff; + } + // 如果不是LATIN1编码格式(大概率是UTF-16编码格式),则调用StringUTF16类的codePointBeforeSB方法来获取指定索引位置前一个位置处的代码点,该方法内部会根据UTF-16编码以及字符串构建器的相关特性进行相应的代码点提取操作 + return StringUTF16.codePointBeforeSB(value, index); +} + +// codePointCount方法用于计算给定起始索引(beginIndex)和结束索引(endIndex)之间包含的代码点数量 +// 参数beginIndex表示起始字符索引,endIndex表示结束字符索引,计算这两个索引范围内的代码点个数 +public int codePointCount(int beginIndex, int endIndex) { + // 调用Preconditions类的checkFromToIndex方法对传入的起始索引(beginIndex)和结束索引(endIndex)进行合法性校验,确保它们在有效字符范围(基于当前对象的长度length())内,避免出现越界等异常情况,同时保证起始索引小于等于结束索引 + Preconditions.checkFromToIndex(beginIndex, endIndex, length(), null); + // 判断当前对象存储内容是否采用的是LATIN1编码格式,如果是,由于在LATIN1编码下一个字节代表一个字符对应的代码点,所以代码点数量就等于结束索引减去起始索引的差值 + if (isLatin1()) { + return endIndex - beginIndex; + } + // 如果不是LATIN1编码格式(大概率是UTF-16编码格式),则调用StringUTF16类的codePointCountSB方法来计算给定索引范围内的代码点数量,该方法内部会根据UTF-16编码以及字符串构建器的相关特性进行相应的代码点数量统计操作 + return StringUTF16.codePointCountSB(value, beginIndex, endIndex); +} + +// offsetByCodePoints方法用于根据给定的代码点偏移量(codePointOffset)来计算相对于指定索引(index)的新索引位置 +// 参数index表示起始索引位置,codePointOffset表示代码点的偏移量(可以是正数或负数,用于在代码点层面上移动索引位置) +public int offsetByCodePoints(int index, int codePointOffset) { + // 首先对传入的起始索引(index)进行合法性校验,如果索引小于0或者大于当前对象中有效字符的数量(count),说明不符合要求,抛出IndexOutOfBoundsException异常,防止出现越界访问等问题 + if (index < 0 || index > count) { + throw new IndexOutOfBoundsException(); + } + // 调用Character类的offsetByCodePoints方法,传入当前对象(this)、起始索引(index)以及代码点偏移量(codePointOffset),该方法会根据字符和代码点的相关规则来计算出偏移后的新索引位置,并返回该位置 + return Character.offsetByCodePoints(this, + index, codePointOffset); +} + +// getChars方法用于将当前对象中指定范围的字符复制到给定的目标字符数组(dst)中指定的起始位置(dstBegin) +// 参数srcBegin表示源字符串(即当前对象存储的字符串内容)中要开始复制字符的起始索引,srcEnd表示源字符串中结束复制字符的结束索引,dst表示目标字符数组,dstBegin表示在目标字符数组中开始存放字符的起始位置 +public void getChars(int srcBegin, int srcEnd, char[] dst, int dstBegin) +{ + // 调用Preconditions类的checkFromToIndex方法对传入的源字符串起始索引(srcBegin)和结束索引(srcEnd)进行合法性校验,确保它们在当前对象有效字符范围(基于count)内,同时保证起始索引小于等于结束索引,并且异常信息格式符合旧版本兼容性要求(通过传入Preconditions.SIOOBE_FORMATTER参数体现) + Preconditions.checkFromToIndex(srcBegin, srcEnd, count, Preconditions.SIOOBE_FORMATTER); + // 计算要复制的字符数量,即结束索引减去起始索引的差值 + int n = srcEnd - srcBegin; + // 调用Preconditions类的checkFromToIndex方法对目标字符数组的起始索引(dstBegin)以及复制后结束的索引(dstBegin + n)进行合法性校验,确保它们在目标字符数组的有效长度(dst.length)范围内,避免出现越界访问等异常情况,异常信息格式通过传入Preconditions.IOOBE_FORMATTER参数指定 + Preconditions.checkFromToIndex(dstBegin, dstBegin + n, dst.length, Preconditions.IOOBE_FORMATTER); + // 判断当前对象存储内容是否采用的是LATIN1编码格式,如果是,调用StringLatin1类的getChars方法来将当前对象中指定范围的字符复制到目标字符数组中,传入相应的参数(源字节数组、源字符串起始索引、源字符串结束索引、目标字符数组、目标字符数组起始索引)进行字符复制操作 + if (isLatin1()) { + StringLatin1.getChars(value, srcBegin, srcEnd, dst, dstBegin); + } + // 如果不是LATIN1编码格式(大概率是UTF-16编码格式),则调用StringUTF16类的getChars方法来进行相应的字符复制操作,同样传入对应的参数来完成复制过程 + else { + StringUTF16.getChars(value, srcBegin, srcEnd, dst, dstBegin); + } +} +// setCharAt方法用于将指定索引位置(index)处的字符设置为给定的字符(ch) +// 参数index表示要设置字符的位置索引,ch表示要设置的具体字符 +public void setCharAt(int index, char ch) { + // 首先调用checkIndex方法对传入的索引(index)进行合法性校验,确保索引在当前对象有效字符数量范围(count)内,避免出现越界访问等异常情况 + checkIndex(index, count); + // 判断当前对象存储内容是否采用的是LATIN1编码格式,并且传入的要设置的字符(ch)是否能够用LATIN1编码表示(通过调用StringLatin1.canEncode方法进行判断) + if (isLatin1() && StringLatin1.canEncode(ch)) { + // 如果满足上述条件,直接将字节数组(value)中对应索引位置的字节设置为给定字符(ch)转换后的字节值(通过强制类型转换为byte类型),完成字符设置操作 + value[index] = (byte)ch; + } else { + // 如果不满足上述条件,说明可能需要处理更复杂的编码情况(比如从LATIN1转换为其他编码格式来存储字符) + if (isLatin1()) { + // 如果当前对象原本是LATIN1编码格式,先调用inflate方法将其转换为UTF16编码格式(通常UTF16能处理更广泛的字符情况),以适应要设置的字符的编码需求 + inflate(); + } + // 调用StringUTF16类的putCharSB方法,将给定字符(ch)按照UTF16编码格式存储到字节数组(value)中对应索引(index)位置处,该方法内部会根据UTF16编码规则进行相应的存储操作 + StringUTF16.putCharSB(value, index, ch); + // 将maybeLatin1标志位设置为true(具体含义与字符串是否可能采用LATIN1编码相关,可能是一种优化相关的判断标志,此处设置为true可能表示后续有可能再转换回LATIN1编码等情况) + maybeLatin1 = true; + } +} + +// append方法(接收Object类型参数)用于将给定的对象(obj)转换为字符串后追加到当前AbstractStringBuilder对象的末尾 +// 它实际上是通过先调用String.valueOf方法将对象转换为字符串,再调用另一个接收字符串参数的append方法来实现追加功能 +public AbstractStringBuilder append(Object obj) { + return append(String.valueOf(obj)); +} + +// append方法(接收String类型参数)用于将给定的字符串(str)追加到当前AbstractStringBuilder对象的末尾 +// 参数str表示要追加的字符串 +public AbstractStringBuilder append(String str) { + // 如果传入的字符串为null,调用appendNull方法来处理将null值追加到字符串构建器中的情况,然后返回当前对象(this) + if (str == null) { + return appendNull(); + } + // 获取传入字符串的长度,后续用于容量计算等操作 + int len = str.length(); + // 调用ensureCapacityInternal方法确保当前对象内部存储字符的字节数组有足够的容量来容纳追加字符串后的内容,传入当前已有字符数量(count)与要追加字符串长度(len)之和作为最小容量需求进行容量检查和扩充(如有需要) + ensureCapacityInternal(count + len); + // 调用putStringAt方法将传入的字符串(str)放置到当前对象内部字节数组的合适位置(从当前已有字符数量的位置开始放置),实现字符串的追加操作 + putStringAt(count, str); + // 更新当前对象中已存储的有效字符数量,将其加上要追加字符串的长度(len),反映追加操作后的字符数量变化 + count += len; + // 返回当前对象(this),方便链式调用,例如可以连续多次调用append方法进行多个字符串的追加操作 + return this; +} + +// append方法(接收AbstractStringBuilder类型参数)用于将另一个AbstractStringBuilder对象(asb)中的内容追加到当前AbstractStringBuilder对象的末尾 +// 参数asb表示要追加其内容的另一个AbstractStringBuilder对象 +AbstractStringBuilder append(AbstractStringBuilder asb) { + // 如果传入的AbstractStringBuilder对象为null,调用appendNull方法来处理将null值对应的表示追加到字符串构建器中的情况,然后返回当前对象(this) + if (asb == null) { + return appendNull(); + } + // 获取传入的AbstractStringBuilder对象的长度(即其包含的有效字符数量),后续用于容量计算等操作 + int len = asb.length(); + // 调用ensureCapacityInternal方法确保当前对象内部存储字符的字节数组有足够的容量来容纳追加内容后的情况,传入当前已有字符数量(count)与要追加的AbstractStringBuilder对象的长度(len)之和作为最小容量需求进行容量检查和扩充(如有需要) + ensureCapacityInternal(count + len); + // 调用inflateIfNeededFor方法,根据传入的AbstractStringBuilder对象(asb)的编码情况等,判断是否需要对当前对象进行编码格式转换等操作(比如从LATIN1转换为UTF16以适应追加内容的编码需求),如有需要则进行相应转换 + inflateIfNeededFor(asb); + // 调用传入的AbstractStringBuilder对象(asb)的getBytes方法,将其内部存储的字符内容按照当前对象的编码格式(coder)提取出来,并放置到当前对象的字节数组(value)中从当前已有字符数量(count)位置开始的地方,实现内容追加操作 + asb.getBytes(value, count, coder); + // 更新当前对象中已存储的有效字符数量,将其加上要追加的AbstractStringBuilder对象的长度(len),反映追加操作后的字符数量变化 + count += len; + // 使用逻辑或操作合并当前对象与传入的AbstractStringBuilder对象的maybeLatin1标志位状态,该标志位可能与后续判断字符串是否可能采用LATIN1编码相关,用于一些优化或编码转换相关的逻辑判断 + maybeLatin1 |= asb.maybeLatin1; + // 返回当前对象(this),方便链式调用,例如可以连续追加多个AbstractStringBuilder对象的内容 + return this; +} + +// @Override注解表示此方法重写了父类(或实现的接口中定义的)同名方法,用于将给定的CharSequence类型的对象(s)追加到当前AbstractStringBuilder对象的末尾 +// 参数s表示要追加的CharSequence对象,CharSequence是一个表示字符序列的接口,像String、AbstractStringBuilder等都实现了它 +@Override +public AbstractStringBuilder append(CharSequence s) { + // 如果传入的CharSequence对象为null,调用appendNull方法来处理将null值对应的表示追加到字符串构建器中的情况,然后返回当前对象(this) + if (s == null) { + return appendNull(); + } + // 如果传入的CharSequence对象是String类型的实例,直接调用当前对象的另一个接收String类型参数的append方法来进行字符串追加操作 + if (s instanceof String) { + return this.append((String)s); + } + // 如果传入的CharSequence对象是AbstractStringBuilder类型的实例,直接调用当前对象的另一个接收AbstractStringBuilder类型参数的append方法来进行内容追加操作 + if (s instanceof AbstractStringBuilder) { + return this.append((AbstractStringBuilder)s); + } + // 如果传入的CharSequence对象既不是String也不是AbstractStringBuilder类型,调用当前对象的append方法(传入CharSequence对象、起始索引0以及长度s.length())来进行通用的字符序列追加操作 + return this.append(s, 0, s.length()); +} + +// appendNull方法用于处理将null值对应的表示追加到当前AbstractStringBuilder对象中的情况 +// 它会先确保内部字节数组有足够容量,然后根据当前对象的编码格式(是LATIN1还是其他编码格式)将"null"字符串对应的字符以相应编码方式存储到字节数组中合适位置,并更新有效字符数量,最后返回当前对象(this) +private AbstractStringBuilder appendNull() { + // 调用ensureCapacityInternal方法确保当前对象内部存储字符的字节数组有足够的容量来容纳追加"null"字符串后的内容,传入当前已有字符数量(count)与"null"字符串长度(4)之和作为最小容量需求进行容量检查和扩充(如有需要) + ensureCapacityInternal(count + 4); + // 获取当前对象中已存储的有效字符数量,用于后续操作中确定存储"null"字符串对应字符的起始位置等 + int count = this.count; + // 获取当前对象内部存储字符的字节数组,用于将"null"字符串对应的字符存储进去 + byte[] val = this.value; + // 判断当前对象存储内容是否采用的是LATIN1编码格式 + if (isLatin1()) { + // 如果是LATIN1编码格式,按照顺序将"null"字符串中每个字符对应的字节值依次存储到字节数组(val)中从当前有效字符数量(count)位置开始的地方,并依次更新有效字符数量(count++) + val[count++] = 'n'; + val[count++] = 'u'; + val[count++] = 'l'; + val[count++] = 'l'; + } else { + // 如果不是LATIN1编码格式(大概率是UTF16编码格式),调用StringUTF16类的putCharsAt方法,将"null"字符串中每个字符按照UTF16编码格式存储到字节数组(val)中从当前有效字符数量(count)位置开始的地方,并返回更新后的有效字符数量,用于更新当前对象的有效字符数量(this.count) + count = StringUTF16.putCharsAt(val, count, 'n', 'u', 'l', 'l'); + } + // 更新当前对象中已存储的有效字符数量为上述操作后最终的count值,反映追加"null"字符串后的字符数量变化 + this.count = count; + // 返回当前对象(this),以保持链式调用的一致性等要求 + return this; +} +// @Override注解表示此方法重写了父类(或实现的接口中定义的)同名方法,用于将给定的CharSequence对象(s)中指定范围(从start索引到end索引)的字符序列追加到当前AbstractStringBuilder对象的末尾 +// 参数s表示要从中获取字符序列进行追加的CharSequence对象,start表示起始索引,end表示结束索引,指定了要追加的字符范围 +@Override +public AbstractStringBuilder append(CharSequence s, int start, int end) { + // 如果传入的CharSequence对象为null,将其替换为字符串"null",以便后续按照处理非null的CharSequence对象的逻辑进行操作,把"null"对应的字符序列追加到当前字符串构建器中 + if (s == null) { + s = "null"; + } + // 调用Preconditions类的checkFromToIndex方法对传入的起始索引(start)和结束索引(end)进行合法性校验,确保它们在给定的CharSequence对象(s)的有效长度范围内,并且异常信息格式按照Preconditions.IOOBE_FORMATTER指定的格式来设置,避免出现越界访问等异常情况 + Preconditions.checkFromToIndex(start, end, s.length(), Preconditions.IOOBE_FORMATTER); + // 计算要追加的字符序列的长度,即结束索引减去起始索引的差值 + int len = end - start; + // 调用ensureCapacityInternal方法确保当前对象内部存储字符的字节数组有足够的容量来容纳追加指定字符序列后的内容,传入当前已有字符数量(count)与要追加的字符序列长度(len)之和作为最小容量需求进行容量检查和扩充(如有需要) + ensureCapacityInternal(count + len); + // 如果传入的CharSequence对象是String类型的实例,调用appendChars方法(传入对应的String对象以及起始、结束索引)来进行字符串中指定范围字符的追加操作,该方法内部会根据具体编码格式等进行相应处理 + if (s instanceof String) { + appendChars((String)s, start, end); + } else { + // 如果传入的CharSequence对象不是String类型,同样调用appendChars方法(传入该CharSequence对象以及起始、结束索引)来进行通用的字符序列追加操作,内部会根据对象的具体情况及编码格式等进行处理 + appendChars(s, start, end); + } + // 返回当前对象(this),方便链式调用,例如可以连续多次调用append方法追加不同的字符序列 + return this; +} + +// append方法用于将给定的字符数组(str)中的所有字符追加到当前AbstractStringBuilder对象的末尾 +// 参数str表示要追加的字符数组 +public AbstractStringBuilder append(char[] str) { + // 获取传入字符数组的长度,后续用于容量计算等操作 + int len = str.length; + // 调用ensureCapacityInternal方法确保当前对象内部存储字符的字节数组有足够的容量来容纳追加整个字符数组内容后的情况,传入当前已有字符数量(count)与字符数组长度(len)之和作为最小容量需求进行容量检查和扩充(如有需要) + ensureCapacityInternal(count + len); + // 调用appendChars方法(传入字符数组、起始索引0以及长度len,即表示追加整个字符数组的内容)来进行字符数组中所有字符的追加操作,该方法内部会根据具体编码格式等进行相应处理 + appendChars(str, 0, len); + // 返回当前对象(this),方便链式调用,例如可以连续追加多个字符数组的内容 + return this; +} + +// append方法用于将给定的字符数组(str)中指定范围(从offset索引开始,长度为len)的字符追加到当前AbstractStringBuilder对象的末尾 +// 参数str表示要从中获取字符进行追加的字符数组,offset表示起始索引,len表示要追加的字符数量,通过这两个参数确定了要追加的字符范围 +public AbstractStringBuilder append(char[] str, int offset, int len) { + // 计算出要追加的字符范围的结束索引,即起始索引(offset)加上要追加的字符数量(len) + int end = offset + len; + // 调用Preconditions类的checkFromToIndex方法对传入的起始索引(offset)和结束索引(end)进行合法性校验,确保它们在给定的字符数组(str)的有效长度范围内,并且异常信息格式按照Preconditions.IOOBE_FORMATTER指定的格式来设置,避免出现越界访问等异常情况 + Preconditions.checkFromToIndex(offset, end, str.length, Preconditions.IOOBE_FORMATTER); + // 调用ensureCapacityInternal方法确保当前对象内部存储字符的字节数组有足够的容量来容纳追加指定范围字符后的内容,传入当前已有字符数量(count)与要追加的字符数量(len)之和作为最小容量需求进行容量检查和扩充(如有需要) + ensureCapacityInternal(count + len); + // 调用appendChars方法(传入字符数组、起始索引offset以及结束索引end,即表示追加指定范围的字符内容)来进行字符数组中指定范围字符的追加操作,该方法内部会根据具体编码格式等进行相应处理 + appendChars(str, offset, end); + // 返回当前对象(this),方便链式调用,例如可以连续追加不同范围的字符数组内容 + return this; +} + +// append方法用于将给定的布尔值(b)转换为对应的字符串表示("true"或"false")后追加到当前AbstractStringBuilder对象的末尾 +// 参数b表示要追加的布尔值 +public AbstractStringBuilder append(boolean b) { + // 根据布尔值(b)的情况确定追加"true"或"false"字符串所需的最小容量,调用ensureCapacityInternal方法确保当前对象内部存储字符的字节数组有足够的容量来容纳追加后的内容,传入当前已有字符数量(count)与对应的字符串长度(b为true时长度为4,b为false时长度为5)之和作为最小容量需求进行容量检查和扩充(如有需要) + ensureCapacityInternal(count + (b? 4 : 5)); + // 获取当前对象中已存储的有效字符数量,用于后续操作中确定存储布尔值对应的字符串字符的起始位置等 + int count = this.count; + // 获取当前对象内部存储字符的字节数组,用于将布尔值对应的字符串字符存储进去 + byte[] val = this.value; + // 判断当前对象存储内容是否采用的是LATIN1编码格式 + if (isLatin1()) { + // 如果是LATIN1编码格式,根据布尔值(b)的情况,按照顺序将"true"或"false"字符串中每个字符对应的字节值依次存储到字节数组(val)中从当前有效字符数量(count)位置开始的地方,并依次更新有效字符数量(count++) + if (b) { + val[count++] = 't'; + val[count++] = 'r'; + val[count++] = 'u'; + val[count++] = 'e'; + } else { + val[count++] = 'f'; + val[count++] = 'a'; + val[count++] = 'l'; + val[count++] = 's'; + val[count++] = 'e'; + } + } else { + // 如果不是LATIN1编码格式(大概率是UTF16编码格式),根据布尔值(b)的情况,调用StringUTF16类的putCharsAt方法,将"true"或"false"字符串中每个字符按照UTF16编码格式存储到字节数组(val)中从当前有效字符数量(count)位置开始的地方,并返回更新后的有效字符数量,用于更新当前对象的有效字符数量(this.count) + if (b) { + count = StringUTF16.putCharsAt(val, count, 't', 'r', 'u', 'e'); + } else { + count = StringUTF16.putCharsAt(val, count, 'f', 'a', 'l', 's', 'e'); + } + } + // 更新当前对象中已存储的有效字符数量为上述操作后最终的count值,反映追加布尔值对应的字符串后的字符数量变化 + this.count = count; + // 返回当前对象(this),方便链式调用,例如可以连续追加多个布尔值对应的字符串表示 + return this; +} + +// @Override注解表示此方法重写了父类(或实现的接口中定义的)同名方法,用于将给定的单个字符(c)追加到当前AbstractStringBuilder对象的末尾 +// 参数c表示要追加的单个字符 +@Override +public AbstractStringBuilder append(char c) { + // 调用ensureCapacityInternal方法确保当前对象内部存储字符的字节数组有足够的容量来容纳追加单个字符后的内容,传入当前已有字符数量(count)与1(单个字符的长度)之和作为最小容量需求进行容量检查和扩充(如有需要) + ensureCapacityInternal(count + 1); + // 判断当前对象存储内容是否采用的是LATIN1编码格式,并且传入的要追加的字符(c)是否能够用LATIN1编码表示(通过调用StringLatin1.canEncode方法进行判断) + if (isLatin1() && StringLatin1.canEncode(c)) { + // 如果满足上述条件,直接将字节数组(value)中对应索引位置(当前已有字符数量count处)的字节设置为给定字符(c)转换后的字节值(通过强制类型转换为byte类型),并更新有效字符数量(count++),完成单个字符的追加操作 + value[count++] = (byte)c; + } else { + // 如果不满足上述条件,说明可能需要处理更复杂的编码情况(比如从LATIN1转换为其他编码格式来存储字符) + if (isLatin1()) { + // 如果当前对象原本是LATIN1编码格式,先调用inflate方法将其转换为UTF16编码格式(通常UTF16能处理更广泛的字符情况),以适应要追加字符的编码需求 + inflate(); + } + // 调用StringUTF16类的putCharSB方法,将给定字符(c)按照UTF16编码格式存储到字节数组(value)中当前已有字符数量(count)位置处,然后更新有效字符数量(count++),该方法内部会根据UTF16编码规则进行相应的存储操作 + StringUTF16.putCharSB(value, count++, c); + } + // 返回当前对象(this),方便链式调用,例如可以连续追加多个单个字符 + return this; +} + +// append方法用于将给定的整数(i)转换为对应的字符串表示后追加到当前AbstractStringBuilder对象的末尾 +// 参数i表示要追加的整数 +public AbstractStringBuilder append(int i) { + // 获取当前对象中已存储的有效字符数量,用于后续操作中确定存储整数对应的字符串字符的起始位置等 + int count = this.count; + // 调用Integer类的stringSize方法计算将给定整数(i)转换为字符串表示后所需的字符数量,再加上当前已有字符数量(count)得到追加整数对应的字符串后总共需要的空间大小(spaceNeeded) + int spaceNeeded = count + Integer.stringSize(i); + // 调用ensureCapacityInternal方法确保当前对象内部存储字符的字节数组有足够的容量来容纳追加整数对应的字符串后的内容,传入spaceNeeded作为最小容量需求进行容量检查和扩充(如有需要) + ensureCapacityInternal(spaceNeeded); + // 判断当前对象存储内容是否采用的是LATIN1编码格式 + if (isLatin1()) { + // 如果是LATIN1编码格式,调用StringLatin1类的getChars方法,将给定整数(i)转换为对应的字符串字符,并按照LATIN1编码格式存储到字节数组(value)中从当前已有字符数量(count)位置开始的地方,完成整数到字符串的转换及追加操作,该方法内部会根据LATIN1编码规则进行相应处理 + StringLatin1.getChars(i, spaceNeeded, value); + } else { + // 如果不是LATIN1编码格式(大概率是UTF16编码格式),调用StringUTF16类的getChars方法,将给定整数(i)转换为对应的字符串字符,并按照UTF16编码格式存储到字节数组(value)中从当前已有字符数量(count)位置开始的地方,完成整数到字符串的转换及追加操作,该方法内部会根据UTF16编码规则进行相应处理 + StringUTF16.getChars(i, count, spaceNeeded, value); + } + // 更新当前对象中已存储的有效字符数量为追加整数对应的字符串后的最终字符数量(spaceNeeded),反映追加操作后的字符数量变化 + this.count = spaceNeeded; + // 返回当前对象(this),方便链式调用,例如可以连续追加多个整数对应的字符串表示 + return this; +} +// append方法用于将给定的长整型数值(l)转换为对应的字符串表示后追加到当前AbstractStringBuilder对象的末尾 +// 参数l表示要追加的长整型数值 +public AbstractStringBuilder append(long l) { + // 获取当前对象中已存储的有效字符数量,用于后续操作中确定存储长整型数值对应的字符串字符的起始位置等 + int count = this.count; + // 调用Long类的stringSize方法计算将给定长整型数值(l)转换为字符串表示后所需的字符数量,再加上当前已有字符数量(count)得到追加长整型数值对应的字符串后总共需要的空间大小(spaceNeeded) + int spaceNeeded = count + Long.stringSize(l); + // 调用ensureCapacityInternal方法确保当前对象内部存储字符的字节数组有足够的容量来容纳追加长整型数值对应的字符串后的内容,传入spaceNeeded作为最小容量需求进行容量检查和扩充(如有需要) + ensureCapacityInternal(spaceNeeded); + // 判断当前对象存储内容是否采用的是LATIN1编码格式 + if (isLatin1()) { + // 如果是LATIN1编码格式,调用StringLatin1类的getChars方法,将给定长整型数值(l)转换为对应的字符串字符,并按照LATIN1编码格式存储到字节数组(value)中从当前已有字符数量(count)位置开始的地方,完成长整型数值到字符串的转换及追加操作,该方法内部会根据LATIN1编码规则进行相应处理 + StringLatin1.getChars(l, spaceNeeded, value); + } else { + // 如果不是LATIN1编码格式(大概率是UTF16编码格式),调用StringUTF16类的getChars方法,将给定长整型数值(l)转换为对应的字符串字符,并按照UTF16编码格式存储到字节数组(value)中从当前已有字符数量(count)位置开始的地方,完成长整型数值到字符串的转换及追加操作,该方法内部会根据UTF16编码规则进行相应处理 + StringUTF16.getChars(l, count, spaceNeeded, value); + } + // 更新当前对象中已存储的有效字符数量为追加长整型数值对应的字符串后的最终字符数量(spaceNeeded),反映追加操作后的字符数量变化 + this.count = spaceNeeded; + // 返回当前对象(this),方便链式调用,例如可以连续追加多个长整型数值对应的字符串表示 + return this; +} + +// append方法用于将给定的单精度浮点数(f)转换为对应的字符串表示后追加到当前AbstractStringBuilder对象的末尾 +// 参数f表示要追加的单精度浮点数 +public AbstractStringBuilder append(float f) { + try { + // 调用FloatToDecimal类的appendTo方法尝试将给定的单精度浮点数(f)转换为字符串并追加到当前AbstractStringBuilder对象中,该方法内部会按照浮点数转换字符串的相关规则进行处理 + FloatToDecimal.appendTo(f, this); + } catch (IOException e) { + // 如果在转换追加过程中出现IOException异常(通常表示在底层进行一些输入输出相关操作时出现问题,虽然此处并非典型的文件读写等输入输出场景,但可能是数据格式转换等操作引发了类似异常情况),则抛出AssertionError异常,并将捕获到的IOException异常作为原因传递进去,这表示出现了不符合预期的错误情况 + throw new AssertionError(e); + } + // 返回当前对象(this),方便链式调用,例如可以连续追加多个浮点数对应的字符串表示 + return this; +} + +// append方法用于将给定的双精度浮点数(d)转换为对应的字符串表示后追加到当前AbstractStringBuilder对象的末尾 +// 参数d表示要追加的双精度浮点数 +public AbstractStringBuilder append(double d) { + try { + // 调用DoubleToDecimal类的appendTo方法尝试将给定的双精度浮点数(d)转换为字符串并追加到当前AbstractStringBuilder对象中,该方法内部会按照双精度浮点数转换字符串的相关规则进行处理 + DoubleToDecimal.appendTo(d, this); + } catch (IOException e) { + // 如果在转换追加过程中出现IOException异常(通常表示在底层进行一些输入输出相关操作时出现问题,虽然此处并非典型的文件读写等输入输出场景,但可能是数据格式转换等操作引发了类似异常情况),则抛出AssertionError异常,并将捕获到的IOException异常作为原因传递进去,这表示出现了不符合预期的错误情况 + throw new AssertionError(e); + } + // 返回当前对象(this),方便链式调用,例如可以连续追加多个浮点数对应的字符串表示 + return this; +} + +// delete方法用于删除当前AbstractStringBuilder对象中指定范围(从start索引到end索引)的字符 +// 参数start表示要删除字符范围的起始索引,end表示要删除字符范围的结束索引 +public AbstractStringBuilder delete(int start, int end) { + // 获取当前对象中已存储的有效字符数量,方便后续进行边界判断等操作 + int count = this.count; + // 如果传入的结束索引(end)大于当前对象中已存储的有效字符数量(count),说明要删除的范围超出了实际字符范围,将结束索引修正为当前有效字符数量,即只删除到实际字符末尾为止 + if (end > count) { + end = count; + } + // 调用Preconditions类的checkFromToIndex方法对传入的起始索引(start)和结束索引(end)进行合法性校验,确保它们在当前对象的有效字符数量范围(count)内,并且异常信息格式按照Preconditions.SIOOBE_FORMATTER指定的格式来设置,避免出现越界访问等异常情况 + Preconditions.checkFromToIndex(start, end, count, Preconditions.SIOOBE_FORMATTER); + // 计算要删除的字符数量,即结束索引减去起始索引的差值 + int len = end - start; + // 如果要删除的字符数量大于0,说明确实有字符需要删除,调用shift方法(传入结束索引和要移动的字符数量 -len,表示向左移动相应字符数量的位置)来移动剩余字符,覆盖要删除的字符部分 + if (len > 0) { + shift(end, -len); + // 更新当前对象中已存储的有效字符数量,将其减去删除的字符数量(len),反映删除操作后的字符数量变化 + this.count = count - len; + // 将maybeLatin1标志位设置为true(具体含义与字符串是否可能采用LATIN1编码相关,可能是一种优化相关的判断标志,此处设置为true可能表示删除操作后字符串的编码特性有了变化等情况) + maybeLatin1 = true; + } + // 返回当前对象(this),方便链式调用,例如可以连续进行多次删除或其他操作 + return this; +} + +// appendCodePoint方法用于将给定的代码点(codePoint)对应的字符或字符序列追加到当前AbstractStringBuilder对象的末尾 +// 参数codePoint表示要追加的代码点(在Unicode中,一个代码点可以对应一个字符,对于一些复杂字符可能由多个代码单元组成) +public AbstractStringBuilder appendCodePoint(int codePoint) { + // 调用Character类的isBmpCodePoint方法判断给定的代码点(codePoint)是否属于基本多文种平面(BMP,Basic Multilingual Plane,Unicode中常用字符大多在此平面内,其代码点范围是U+0000到U+FFFF,在此平面内一个代码点通常可以用一个16位的字符表示)内的代码点 + if (Character.isBmpCodePoint(codePoint)) { + // 如果是BMP内的代码点,通过强制类型转换将代码点转换为字符(char类型),然后调用当前对象的另一个接收字符参数的append方法将该字符追加到字符串构建器末尾 + return append((char)codePoint); + } + // 如果不是BMP内的代码点,说明是需要用多个代码单元表示的复杂字符,调用Character类的toChars方法将代码点转换为对应的字符序列(char数组),再调用当前对象的接收CharSequence类型参数的append方法将该字符序列追加到字符串构建器末尾 + return append(Character.toChars(codePoint)); +} + +// deleteCharAt方法用于删除当前AbstractStringBuilder对象中指定索引位置(index)处的单个字符 +// 参数index表示要删除字符的位置索引 +public AbstractStringBuilder deleteCharAt(int index) { + // 首先调用checkIndex方法对传入的索引(index)进行合法性校验,确保索引在当前对象有效字符数量范围(count)内,避免出现越界访问等异常情况 + checkIndex(index, count); + // 调用shift方法(传入索引加1(因为要从指定索引的下一个位置开始移动字符来覆盖要删除的字符)和要移动的字符数量 -1,表示向左移动一个字符的位置)来移动剩余字符,覆盖要删除的字符位置 + shift(index + 1, -1); + // 更新当前对象中已存储的有效字符数量,将其减1,反映删除单个字符操作后的字符数量变化 + count--; + // 将maybeLatin1标志位设置为true(具体含义与字符串是否可能采用LATIN1编码相关,可能是一种优化相关的判断标志,此处设置为true可能表示删除操作后字符串的编码特性有了变化等情况) + maybeLatin1 = true; + // 返回当前对象(this),方便链式调用,例如可以连续进行多次删除字符或其他操作 + return this; +} + +// replace方法用于将当前AbstractStringBuilder对象中指定范围(从start索引到end索引)的字符替换为给定的字符串(str) +// 参数start表示要替换字符范围的起始索引,end表示要替换字符范围的结束索引,str表示用于替换的字符串 +public AbstractStringBuilder replace(int start, int end, String str) { + // 获取当前对象中已存储的有效字符数量,方便后续进行边界判断、容量计算等操作 + int count = this.count; + // 如果传入的结束索引(end)大于当前对象中已存储的有效字符数量(count),说明要替换的范围超出了实际字符范围,将结束索引修正为当前有效字符数量,即只替换到实际字符末尾为止 + if (end > count) { + end = count; + } + // 调用Preconditions类的checkFromToIndex方法对传入的起始索引(start)和结束索引(end)进行合法性校验,确保它们在当前对象的有效字符数量范围(count)内,并且异常信息格式按照Preconditions.SIOOBE_FORMATTER指定的格式来设置,避免出现越界访问等异常情况 + Preconditions.checkFromToIndex(start, end, count, Preconditions.SIOOBE_FORMATTER); + // 获取用于替换的字符串(str)的长度,后续用于计算替换操作后的字符数量等情况 + int len = str.length(); + // 计算替换操作完成后字符串构建器中最终的字符数量,通过当前已有字符数量(count)加上用于替换的字符串长度(len),再减去被替换掉的字符数量(end - start)来得到 + int newCount = count + len - (end - start); + // 调用ensureCapacityInternal方法确保当前对象内部存储字符的字节数组有足够的容量来容纳替换操作后的内容,传入newCount作为最小容量需求进行容量检查和扩充(如有需要) + ensureCapacityInternal(newCount); + // 调用shift方法(传入结束索引和替换操作后与当前字符数量的差值(newCount - count),表示移动剩余字符到合适位置,为插入替换字符串腾出空间)来移动字符位置 + shift(end, newCount - count); + // 更新当前对象中已存储的有效字符数量为替换操作后的最终字符数量(newCount),反映替换操作后的字符数量变化 + this.count = newCount; + // 调用putStringAt方法将用于替换的字符串(str)放置到当前对象内部字节数组的合适位置(从start索引位置开始放置),完成替换操作 + putStringAt(start, str); + // 将maybeLatin1标志位设置为true(具体含义与字符串是否可能采用LATIN1编码相关,可能是一种优化相关的判断标志,此处设置为true可能表示替换操作后字符串的编码特性有了变化等情况) + maybeLatin1 = true; + // 返回当前对象(this),方便链式调用,例如可以连续进行多次替换或其他操作 + return this; +} +// substring方法(接收单个参数)用于获取从指定索引(start)位置开始到字符串末尾的子字符串 +// 参数start表示子字符串的起始索引位置 +public String substring(int start) { + // 直接调用另一个接收两个参数的substring方法,传入起始索引(start)和当前对象中已存储的有效字符数量(count),相当于获取从start索引到字符串末尾的子字符串,将具体的子字符串获取逻辑委托给那个重载的方法处理 + return substring(start, count); +} + +// @Override注解表示此方法重写了父类(或实现的接口中定义的)同名方法,用于获取当前对象中指定范围(从start索引到end索引)的字符序列作为一个CharSequence返回 +// 参数start表示子序列的起始索引,end表示子序列的结束索引,指定了要获取的字符范围 +@Override +public CharSequence subSequence(int start, int end) { + // 同样是调用substring方法(传入start和end索引)来获取指定范围的字符序列,将获取子序列的具体逻辑委托给substring方法处理,因为在这个类中获取子序列和获取子字符串的逻辑基本是一致的,最终返回一个符合要求的字符序列 + return substring(start, end); +} + +// substring方法(接收两个参数)用于获取当前对象中指定范围(从start索引到end索引)的子字符串 +// 参数start表示子字符串的起始索引,end表示子字符串的结束索引,指定了要获取的字符范围 +public String substring(int start, int end) { + // 调用Preconditions类的checkFromToIndex方法对传入的起始索引(start)和结束索引(end)进行合法性校验,确保它们在当前对象的有效字符数量范围(count)内,并且异常信息格式按照Preconditions.SIOOBE_FORMATTER指定的格式来设置,避免出现越界访问等异常情况 + Preconditions.checkFromToIndex(start, end, count, Preconditions.SIOOBE_FORMATTER); + // 判断当前对象存储内容是否采用的是LATIN1编码格式 + if (isLatin1()) { + // 如果是LATIN1编码格式,调用StringLatin1类的newString方法,传入当前对象内部存储字符的字节数组(value)、起始索引(start)以及要获取的子字符串长度(end - start),该方法会根据LATIN1编码规则从字节数组中提取相应的字符内容并构造出一个新的字符串对象作为子字符串返回 + return StringLatin1.newString(value, start, end - start); + } + // 如果不是LATIN1编码格式(大概率是UTF16编码格式),调用StringUTF16类的newString方法,传入相同的参数(字节数组、起始索引、子字符串长度),该方法会按照UTF16编码规则从字节数组中提取字符内容并构造出一个新的字符串对象作为子字符串返回 + return StringUTF16.newString(value, start, end - start); +} + +// shift方法用于将当前对象内部存储字符的字节数组(value)中的字符进行移动操作 +// 参数offset表示从哪个索引位置开始移动字符,n表示要移动的字符数量(可以是正数表示向右移动,负数表示向左移动) +private void shift(int offset, int n) { + // 使用System.arraycopy方法来进行数组元素的复制移动操作,将字节数组(value)中从offset << coder位置开始的元素复制到(offset + n) << coder位置开始的地方,复制的元素数量为(count - offset) << coder个 + // 这里的左移操作(<< coder)与编码格式相关,不同编码格式下字节数组中存储字符的方式和偏移量计算不同,通过这种方式准确地根据编码格式来定位和移动相应的字符数据 + System.arraycopy(value, offset << coder, + value, (offset + n) << coder, (count - offset) << coder); +} + +// insert方法(接收多个参数)用于将给定的字符数组(str)中指定范围(从offset索引开始,长度为len)的字符插入到当前AbstractStringBuilder对象中指定的索引(index)位置处 +// 参数index表示要插入字符的目标索引位置,str表示要插入的字符数组,offset表示字符数组中要插入字符范围的起始索引,len表示要插入的字符数量 +public AbstractStringBuilder insert(int index, char[] str, int offset, + int len) +{ + // 调用checkOffset方法对传入的插入索引(index)进行合法性校验,确保索引在当前对象已有字符数量(count)的合理范围内,避免出现越界插入等异常情况 + checkOffset(index, count); + // 调用Preconditions类的checkFromToIndex方法对传入的字符数组中的起始索引(offset)和结束索引(offset + len)进行合法性校验,确保它们在给定的字符数组(str)的有效长度范围内,并且异常信息格式按照Preconditions.SIOOBE_FORMATTER指定的格式来设置,避免出现越界访问等异常情况 + Preconditions.checkFromToIndex(offset, offset + len, str.length, Preconditions.SIOOBE_FORMATTER); + // 调用ensureCapacityInternal方法确保当前对象内部存储字符的字节数组有足够的容量来容纳插入字符后的内容,传入当前已有字符数量(count)与要插入的字符数量(len)之和作为最小容量需求进行容量检查和扩充(如有需要) + ensureCapacityInternal(count + len); + // 调用shift方法(传入索引(index)和要插入的字符数量(len),表示将从索引位置开始的字符向右移动指定的字符数量个位置,为插入新字符腾出空间)来移动已有字符的位置 + shift(index, len); + // 更新当前对象中已存储的有效字符数量,将其加上要插入的字符数量(len),反映插入操作后的字符数量变化 + count += len; + // 调用putCharsAt方法(传入插入索引(index)、字符数组(str)、字符数组中要插入字符范围的起始索引(offset)以及结束索引(offset + len)),将字符数组中指定范围的字符插入到字节数组(value)中合适的位置(从index索引处开始插入),完成插入操作 + putCharsAt(index, str, offset, offset + len); + // 返回当前对象(this),方便链式调用,例如可以连续进行多次插入或其他操作 + return this; +} + +// insert方法(接收两个参数)用于将给定的对象(obj)转换为字符串后插入到当前AbstractStringBuilder对象中指定的索引(offset)位置处 +// 参数offset表示要插入字符串的目标索引位置,obj表示要插入的对象 +public AbstractStringBuilder insert(int offset, Object obj) { + // 调用String.valueOf方法将给定的对象(obj)转换为字符串,然后调用另一个接收字符串参数的insert方法来进行实际的字符串插入操作,将转换后的字符串插入到指定索引位置处 + return insert(offset, String.valueOf(obj)); +} + +// insert方法(接收两个参数)用于将给定的字符串(str)插入到当前AbstractStringBuilder对象中指定的索引(offset)位置处 +// 参数offset表示要插入字符串的目标索引位置,str表示要插入的字符串 +public AbstractStringBuilder insert(int offset, String str) { + // 调用checkOffset方法对传入的插入索引(offset)进行合法性校验,确保索引在当前对象已有字符数量(count)的合理范围内,避免出现越界插入等异常情况 + checkOffset(offset, count); + // 如果传入的字符串(str)为null,将其替换为字符串"null",以便后续按照处理非null字符串的逻辑进行插入操作,把"null"字符串插入到指定索引位置处 + if (str == null) { + str = "null"; + } + // 获取要插入的字符串的长度,后续用于容量计算等操作 + int len = str.length(); + // 调用ensureCapacityInternal方法确保当前对象内部存储字符的字节数组有足够的容量来容纳插入字符串后的内容,传入当前已有字符数量(count)与要插入的字符串长度(len)之和作为最小容量需求进行容量检查和扩充(如有需要) + ensureCapacityInternal(count + len); + // 调用shift方法(传入索引(offset)和要插入的字符串长度(len),表示将从索引位置开始的字符向右移动指定的字符串长度个位置,为插入新字符串腾出空间)来移动已有字符的位置 + shift(offset, len); + // 更新当前对象中已存储的有效字符数量,将其加上要插入的字符串长度(len),反映插入操作后的字符数量变化 + count += len; + // 调用putStringAt方法(传入插入索引(offset)和要插入的字符串(str)),将字符串插入到字节数组(value)中合适的位置(从offset索引处开始插入),完成插入操作 + putStringAt(offset, str); + // 返回当前对象(this),方便链式调用,例如可以连续进行多次插入不同字符串的操作 + return this; +} + +// insert方法(接收两个参数)用于将给定的字符数组(str)中的所有字符插入到当前AbstractStringBuilder对象中指定的索引(offset)位置处 +// 参数offset表示要插入字符的目标索引位置,str表示要插入的字符数组 +public AbstractStringBuilder insert(int offset, char[] str) { + // 调用checkOffset方法对传入的插入索引(offset)进行合法性校验,确保索引在当前对象已有字符数量(count)的合理范围内,避免出现越界插入等异常情况 + checkOffset(offset, count); + // 获取要插入的字符数组的长度,后续用于容量计算等操作 + int len = str.length; + // 调用ensureCapacityInternal方法确保当前对象内部存储字符的字节数组有足够的容量来容纳插入字符数组内容后的情况,传入当前已有字符数量(count)与字符数组长度(len)之和作为最小容量需求进行容量检查和扩充(如有需要) + ensureCapacityInternal(count + len); + // 调用shift方法(传入索引(offset)和要插入的字符数组长度(len),表示将从索引位置开始的字符向右移动指定的字符数组长度个位置,为插入新字符数组腾出空间)来移动已有字符的位置 + shift(offset, len); + // 更新当前对象中已存储的有效字符数量,将其加上要插入的字符数组长度(len),反映插入操作后的字符数量变化 + count += len; + // 调用putCharsAt方法(传入插入索引(offset)、字符数组(str)、起始索引0以及长度len,即表示将整个字符数组的内容插入到字节数组(value)中合适的位置(从offset索引处开始插入)),完成插入操作 + putCharsAt(offset, str, 0, len); + // 返回当前对象(this),方便链式调用,例如可以连续进行多次插入不同字符数组的操作 + return this; +} +// insert方法(接收两个参数)用于将给定的CharSequence对象(s)插入到当前AbstractStringBuilder对象中指定的索引(dstOffset)位置处 +// 参数dstOffset表示要插入CharSequence对象的目标索引位置,s表示要插入的CharSequence对象 +public AbstractStringBuilder insert(int dstOffset, CharSequence s) { + // 如果传入的CharSequence对象为null,将其替换为字符串"null",以便后续按照处理非null的CharSequence对象的逻辑进行插入操作,把"null"对应的字符序列插入到指定索引位置处 + if (s == null) { + s = "null"; + } + // 调用另一个接收更多参数的insert方法(传入目标索引(dstOffset)、替换后的CharSequence对象(s)以及起始索引0和长度s.length(),即表示插入整个CharSequence对象的内容)来进行实际的字符序列插入操作,将处理具体插入逻辑委托给那个重载的方法 + return this.insert(dstOffset, s, 0, s.length()); +} + +// insert方法(接收四个参数)用于将给定的CharSequence对象(s)中指定范围(从start索引到end索引)的字符序列插入到当前AbstractStringBuilder对象中指定的索引(dstOffset)位置处 +// 参数dstOffset表示要插入字符序列的目标索引位置,s表示要从中获取字符序列进行插入的CharSequence对象,start表示起始索引,end表示结束索引,指定了要插入的字符范围 +public AbstractStringBuilder insert(int dstOffset, CharSequence s, + int start, int end) +{ + // 如果传入的CharSequence对象为null,将其替换为字符串"null",以便后续按照处理非null的CharSequence对象的逻辑进行插入操作,把"null"对应的字符序列插入到指定索引位置处 + if (s == null) { + s = "null"; + } + // 调用checkOffset方法对传入的插入索引(dstOffset)进行合法性校验,确保索引在当前对象已有字符数量(count)的合理范围内,避免出现越界插入等异常情况 + checkOffset(dstOffset, count); + // 调用Preconditions类的checkFromToIndex方法对传入的CharSequence对象(s)中的起始索引(start)和结束索引(end)进行合法性校验,确保它们在给定的CharSequence对象的有效长度范围内,并且异常信息格式按照Preconditions.IOOBE_FORMATTER指定的格式来设置,避免出现越界访问等异常情况 + Preconditions.checkFromToIndex(start, end, s.length(), Preconditions.IOOBE_FORMATTER); + // 计算要插入的字符序列的长度,即结束索引减去起始索引的差值 + int len = end - start; + // 调用ensureCapacityInternal方法确保当前对象内部存储字符的字节数组有足够的容量来容纳插入指定字符序列后的内容,传入当前已有字符数量(count)与要插入的字符序列长度(len)之和作为最小容量需求进行容量检查和扩充(如有需要) + ensureCapacityInternal(count + len); + // 调用shift方法(传入索引(dstOffset)和要插入的字符序列长度(len),表示将从索引位置开始的字符向右移动指定的字符序列长度个位置,为插入新字符序列腾出空间)来移动已有字符的位置 + shift(dstOffset, len); + // 更新当前对象中已存储的有效字符数量,将其加上要插入的字符序列长度(len),反映插入操作后的字符数量变化 + count += len; + // 如果传入的CharSequence对象是String类型的实例,调用putStringAt方法(传入目标索引(dstOffset)、转换后的String对象((String) s)以及起始索引(start)和结束索引(end),即表示将字符串中指定范围的字符插入到字节数组(value)中合适的位置(从dstOffset索引处开始插入))来进行字符串中指定范围字符的插入操作,该方法内部会根据具体编码格式等进行相应处理 + if (s instanceof String) { + putStringAt(dstOffset, (String) s, start, end); + } else { + // 如果传入的CharSequence对象不是String类型,调用putCharsAt方法(传入目标索引(dstOffset)、CharSequence对象(s)以及起始索引(start)和结束索引(end),即表示将字符序列中指定范围的字符插入到字节数组(value)中合适的位置(从dstOffset索引处开始插入))来进行通用的字符序列插入操作,该方法内部会根据对象的具体情况及编码格式等进行处理 + putCharsAt(dstOffset, s, start, end); + } + // 返回当前对象(this),方便链式调用,例如可以连续进行多次插入不同字符序列的操作 + return this; +} + +// insert方法(接收两个参数)用于将给定的布尔值(b)转换为对应的字符串表示后插入到当前AbstractStringBuilder对象中指定的索引(offset)位置处 +// 参数offset表示要插入字符串的目标索引位置,b表示要插入的布尔值 +public AbstractStringBuilder insert(int offset, boolean b) { + // 调用String.valueOf方法将给定的布尔值(b)转换为字符串,然后调用另一个接收字符串参数的insert方法来进行实际的字符串插入操作,将转换后的字符串插入到指定索引位置处 + return insert(offset, String.valueOf(b)); +} + +// insert方法(接收两个参数)用于将给定的单个字符(c)插入到当前AbstractStringBuilder对象中指定的索引(offset)位置处 +// 参数offset表示要插入字符的目标索引位置,c表示要插入的单个字符 +public AbstractStringBuilder insert(int offset, char c) { + // 调用checkOffset方法对传入的插入索引(offset)进行合法性校验,确保索引在当前对象已有字符数量(count)的合理范围内,避免出现越界插入等异常情况 + checkOffset(offset, count); + // 调用ensureCapacityInternal方法确保当前对象内部存储字符的字节数组有足够的容量来容纳插入单个字符后的内容,传入当前已有字符数量(count)与1(单个字符的长度)之和作为最小容量需求进行容量检查和扩充(如有需要) + ensureCapacityInternal(count + 1); + // 调用shift方法(传入索引(offset)和要插入的字符数量(1,表示单个字符),表示将从索引位置开始的字符向右移动一个字符的位置,为插入新字符腾出空间)来移动已有字符的位置 + shift(offset, 1); + // 更新当前对象中已存储的有效字符数量,将其加上1,反映插入单个字符操作后的字符数量变化 + count += 1; + // 判断当前对象存储内容是否采用的是LATIN1编码格式,并且传入的要插入的字符(c)是否能够用LATIN1编码表示(通过调用StringLatin1.canEncode方法进行判断) + if (isLatin1() && StringLatin1.canEncode(c)) { + // 如果满足上述条件,直接将字节数组(value)中对应索引位置(插入索引offset处)的字节设置为给定字符(c)转换后的字节值(通过强制类型转换为byte类型),完成单个字符的插入操作 + value[offset] = (byte)c; + } else { + // 如果不满足上述条件,说明可能需要处理更复杂的编码情况(比如从LATIN1转换为其他编码格式来存储字符) + if (isLatin1()) { + // 如果当前对象原本是LATIN1编码格式,先调用inflate方法将其转换为UTF16编码格式(通常UTF16能处理更广泛的字符情况),以适应要插入字符的编码需求 + inflate(); + } + // 调用StringUTF16类的putCharSB方法,将给定字符(c)按照UTF16编码格式存储到字节数组(value)中插入索引(offset)位置处,该方法内部会根据UTF16编码规则进行相应的存储操作 + StringUTF16.putCharSB(value, offset, c); + } + // 返回当前对象(this),方便链式调用,例如可以连续进行多次插入单个字符的操作 + return this; +} + +// insert方法(接收两个参数)用于将给定的整数(i)转换为对应的字符串表示后插入到当前AbstractStringBuilder对象中指定的索引(offset)位置处 +// 参数offset表示要插入字符串的目标索引位置,i表示要插入的整数 +public AbstractStringBuilder insert(int offset, int i) { + // 调用String.valueOf方法将给定的整数(i)转换为字符串,然后调用另一个接收字符串参数的insert方法来进行实际的字符串插入操作,将转换后的字符串插入到指定索引位置处 + return insert(offset, String.valueOf(i)); +} + +// insert方法(接收两个参数)用于将给定的长整型数值(l)转换为对应的字符串表示后插入到当前AbstractStringBuilder对象中指定的索引(offset)位置处 +// 参数offset表示要插入字符串的目标索引位置,l表示要插入的长整型数值 +public AbstractStringBuilder insert(int offset, long l) { + // 调用String.valueOf方法将给定的长整型数值(l)转换为字符串,然后调用另一个接收字符串参数的insert方法来进行实际的字符串插入操作,将转换后的字符串插入到指定索引位置处 + return insert(offset, String.valueOf(l)); +} + +// insert方法(接收两个参数)用于将给定的单精度浮点数(f)转换为对应的字符串表示后插入到当前AbstractStringBuilder对象中指定的索引(offset)位置处 +// 参数offset表示要插入字符串的目标索引位置,f表示要插入的单精度浮点数 +public AbstractStringBuilder insert(int offset, float f) { + // 调用String.valueOf方法将给定的单精度浮点数(f)转换为字符串,然后调用另一个接收字符串参数的insert方法来进行实际的字符串插入操作,将转换后的字符串插入到指定索引位置处 + return insert(offset, String.valueOf(f)); +} + +// insert方法(接收两个参数)用于将给定的双精度浮点数(d)转换为对应的字符串表示后插入到当前AbstractStringBuilder对象中指定的索引(offset)位置处 +// 参数offset表示要插入字符串的目标索引位置,d表示要插入的双精度浮点数 +public AbstractStringBuilder insert(int offset, double d) { + // 调用String.valueOf方法将给定的双精度浮点数(d)转换为字符串,然后调用另一个接收字符串参数的insert方法来进行实际的字符串插入操作,将转换后的字符串插入到指定索引位置处 + return insert(offset, String.valueOf(d)); +} +// indexOf方法(接收单个参数)用于在当前AbstractStringBuilder对象所表示的字符串中查找给定字符串(str)首次出现的索引位置,默认从字符串开头(索引为0)开始查找 +// 参数str表示要查找的目标字符串 +public int indexOf(String str) { + // 调用另一个接收两个参数的indexOf方法,传入要查找的字符串(str)和起始查找索引0,将具体的查找逻辑委托给那个重载的方法处理,即从索引0位置开始查找目标字符串在当前字符串中的位置 + return indexOf(str, 0); +} + +// indexOf方法(接收两个参数)用于在当前AbstractStringBuilder对象所表示的字符串中查找给定字符串(str)首次出现的索引位置,可指定从哪个索引(fromIndex)位置开始查找 +// 参数str表示要查找的目标字符串,fromIndex表示起始查找索引 +public int indexOf(String str, int fromIndex) { + // 直接调用String类的indexOf静态方法,传入当前对象内部存储字符的字节数组(value)、编码格式(coder)、已存储的有效字符数量(count)、要查找的字符串(str)以及起始查找索引(fromIndex),利用String类中已有的查找逻辑来确定目标字符串在当前字符串中的首次出现位置,并返回该索引值 + return String.indexOf(value, coder, count, str, fromIndex); +} + +// lastIndexOf方法(接收单个参数)用于在当前AbstractStringBuilder对象所表示的字符串中查找给定字符串(str)最后一次出现的索引位置,默认从字符串末尾(索引为count,即当前已存储的有效字符数量)开始往前查找 +// 参数str表示要查找的目标字符串 +public int lastIndexOf(String str) { + // 调用另一个接收两个参数的lastIndexOf方法,传入要查找的字符串(str)和起始查找索引count,将具体的查找逻辑委托给那个重载的方法处理,即从字符串末尾开始往前查找目标字符串在当前字符串中的最后出现位置 + return lastIndexOf(str, count); +} + +// lastIndexOf方法(接收两个参数)用于在当前AbstractStringBuilder对象所表示的字符串中查找给定字符串(str)最后一次出现的索引位置,可指定从哪个索引(fromIndex)位置开始往前查找 +// 参数str表示要查找的目标字符串,fromIndex表示起始查找索引 +public int lastIndexOf(String str, int fromIndex) { + // 直接调用String类的lastIndexOf静态方法,传入当前对象内部存储字符的字节数组(value)、编码格式(coder)、已存储的有效字符数量(count)、要查找的字符串(str)以及起始查找索引(fromIndex),利用String类中已有的查找逻辑来确定目标字符串在当前字符串中的最后出现位置,并返回该索引值 + return String.lastIndexOf(value, coder, count, str, fromIndex); +} + +// reverse方法用于将当前AbstractStringBuilder对象所表示的字符串进行反转操作,即将字符顺序颠倒过来 +public AbstractStringBuilder reverse() { + // 获取当前对象内部存储字符的字节数组,后续用于操作字符反转 + byte[] val = this.value; + // 获取当前对象中已存储的有效字符数量,方便后续循环等操作基于此进行判断和处理 + int count = this.count; + // 计算出有效字符数量减1的值,用于确定循环的边界等情况,在反转操作中常通过这个值来找到对应位置的字符进行交换 + int n = count - 1; + // 判断当前对象存储内容是否采用的是LATIN1编码格式 + if (isLatin1()) { + // 如果是LATIN1编码格式,通过循环从字符串中间位置往前遍历((n - 1) >> 1 是计算中间偏左一点的位置,利用右移操作进行简单的除法取整效果,用于确定循环起始位置),将对称位置的字符进行交换,实现字符串反转 + for (int j = (n - 1) >> 1; j >= 0; j--) { + int k = n - j; + byte cj = val[j]; + val[j] = val[k]; + val[k] = cj; + } + } else { + // 如果不是LATIN1编码格式(大概率是UTF16编码格式),调用StringUTF16类的reverse方法,传入字节数组(val)和有效字符数量(count),该方法内部会根据UTF16编码以及字符串的相关特性来进行字符串的反转操作,处理更复杂编码格式下的字符反转情况 + StringUTF16.reverse(val, count); + } + // 返回当前对象(this),方便链式调用,例如可以在反转后继续进行其他字符串操作 + return this; +} + +// @Override注解表示此方法重写了父类(或实现的接口中定义的)同名抽象方法,用于将当前AbstractStringBuilder对象所表示的字符串转换为标准的String对象并返回,这里是抽象方法,具体实现由子类来完成 +@Override +public abstract String toString(); + +// @Override注解表示此方法重写了父类(或实现的接口中定义的)同名方法,用于获取当前AbstractStringBuilder对象所表示的字符串中各个字符对应的整数值(char类型在Java中本质上是无符号的16位整数,这里将其作为整数流返回),返回一个IntStream对象,便于进行一些基于流的操作和处理 +@Override +public IntStream chars() { + // Reuse String-based spliterator. This requires a supplier to + // capture the value and count when the terminal operation is executed + // 重用基于String的分割迭代器(Spliterator),这需要一个供应商(Supplier)来在终端操作执行时捕获当前对象内部存储字符的字节数组(value)、已存储的有效字符数量(count)以及编码格式(coder)等信息,以确保能正确地基于这些状态来生成对应的字符流,虽然这一组字段读取操作不是原子性的且非线程安全的,但边界检查(bounds checks,通常在具体的分割迭代器内部实现中会有相关校验)会确保不会出现从字节数组中进行不安全的读取操作 + return StreamSupport.intStream( + () -> { + // 获取当前对象内部存储字符的字节数组,用于后续确定字符流的数据源等操作 + byte[] val = this.value; + // 获取当前对象中已存储的有效字符数量,用于界定字符流的范围等情况 + int count = this.count; + // 获取当前对象的编码格式,后续根据编码格式来决定使用哪种具体的分割迭代器实现类 + byte coder = this.coder; + return coder == LATIN1 + ? new StringLatin1.CharsSpliterator(val, 0, count, 0) + : new StringUTF16.CharsSpliterator(val, 0, count, 0); + }, + Spliterator.ORDERED | Spliterator.SIZED | Spliterator.SUBSIZED, + false); +} + +// @Override注解表示此方法重写了父类(或实现的接口中定义的)同名方法,用于获取当前AbstractStringBuilder对象所表示的字符串中各个代码点(Code Point,在Unicode中,一个代码点可以对应一个字符,对于一些复杂字符可能由多个代码单元组成)对应的整数值,返回一个IntStream对象,便于进行一些基于流的操作和处理 +@Override +public IntStream codePoints() { + // Reuse String-based spliterator. This requires a supplier to + // capture the value and count when the terminal operation is executed + // 重用基于String的分割迭代器(Spliterator),这需要一个供应商(Supplier)来在终端操作执行时捕获当前对象内部存储字符的字节数组(value)、已存储的有效字符数量(count)以及编码格式(coder)等信息,以确保能正确地基于这些状态来生成对应的代码点流,虽然这一组字段读取操作不是原子性的且非线程安全的,但边界检查(bounds checks,通常在具体的分割迭代器内部实现中会有相关校验)会确保不会出现从字节数组中进行不安全的读取操作 + return StreamSupport.intStream( + () -> { + // 获取当前对象内部存储字符的字节数组,用于后续确定代码点流的数据源等操作 + byte[] val = this.value; + // 获取当前对象中已存储的有效字符数量,用于界定代码点流的范围等情况 + int count = this.count; + // 获取当前对象的编码格式,后续根据编码格式来决定使用哪种具体的分割迭代器实现类 + byte coder = this.coder; + return coder == LATIN1 + ? new StringLatin1.CharsSpliterator(val, 0, count, 0) + : new StringUTF16.CodePointsSpliterator(val, 0, count, 0); + }, + Spliterator.ORDERED, + false); +} +// getBytes方法用于将当前对象所存储的字符内容按照指定的编码格式复制到目标字节数组中指定的起始位置 +// 参数dst表示目标字节数组,即要将当前对象的字符内容复制到的目标位置所在的数组 +// 参数dstBegin表示在目标字节数组中开始存放字符内容的起始索引位置 +// 参数coder表示目标编码格式,用于判断与当前对象编码格式是否一致,进而决定如何进行复制操作 +void getBytes(byte[] dst, int dstBegin, byte coder) { + // 判断当前对象的编码格式(this.coder)是否和传入的目标编码格式(coder)相同 + if (this.coder == coder) { + // 如果编码格式相同,使用System.arraycopy方法进行字节数组的复制操作 + // value是当前对象内部存储字符的字节数组,从其索引0位置开始复制 + // 复制到目标字节数组dst中,从dstBegin << coder位置开始存放(这里的左移操作 << coder与编码格式相关,不同编码格式下确定具体复制位置的方式不同) + // 要复制的字节数量为count << coder个,count表示当前对象中已存储的有效字符数量(同样,这里的左移操作也是结合编码格式来准确确定字节数量) + System.arraycopy(value, 0, dst, dstBegin << coder, count << coder); + } else { + // 代码注释里指出这种情况是当前对象编码格式为LATIN且目标编码格式为UTF16的情况 + // 调用StringLatin1.inflate方法,将当前以LATIN1编码存储的字节数组(value)中的内容转换并填充到目标字节数组(dst)中 + // 参数0表示从当前对象字节数组(value)的起始位置开始读取内容 + // dstBegin表示在目标字节数组(dst)中开始填充的起始索引位置 + // count表示要转换填充的字符数量,也就是当前对象中已存储的有效字符数量 + StringLatin1.inflate(value, 0, dst, dstBegin, count); + } +} + +// initBytes方法主要用于在反序列化(readObject相关操作)时,根据给定的字符数组以及相关配置来初始化当前对象的字节数组和编码格式 +// 参数value是传入的字符数组,包含了要处理的字符信息 +// 参数off表示在传入的字符数组中的起始偏移量,用于确定从哪个位置开始处理字符 +// 参数len表示要处理的字符数量,即从起始偏移量位置开始,往后的字符个数 +/* for readObject() */ +void initBytes(char[] value, int off, int len) { + if (String.COMPACT_STRINGS) { + // 如果启用了COMPACT_STRINGS机制(这应该是一个预定义的配置标识,用于控制字符串存储的一些优化策略) + // 调用StringUTF16.compress方法,尝试对传入的字符数组(value)进行压缩处理,传入起始偏移量(off)和要处理的字符数量(len) + // 返回处理后的字节数组,这个字节数组将作为当前对象存储字符的新字节数组 + byte[] val = StringUTF16.compress(value, off, len); + // 通过StringUTF16.coderFromArrayLen方法,根据压缩后得到的字节数组(val)的长度以及要处理的字符数量(len)来确定编码格式,并赋值给当前对象的coder变量,用于标识当前对象后续采用的编码方式 + this.coder = StringUTF16.coderFromArrayLen(val, len); + // 将当前对象内部存储字符的字节数组设置为处理后的字节数组(val),完成字节数组的初始化 + this.value = val; + return; + } + // 如果未启用COMPACT_STRINGS机制,将当前对象的编码格式直接设置为UTF16,通常UTF16是一种更通用、能处理更多类型字符的编码格式 + this.coder = UTF16; + // 调用StringUTF16.toBytes方法,将传入的字符数组(value)按照UTF16编码格式转换为字节数组,得到的字节数组赋值给当前对象内部存储字符的字节数组(value),以此完成初始化操作 + this.value = StringUTF16.toBytes(value, off, len); +} + +// getCoder方法用于获取当前对象的编码格式,它是一个final方法,意味着不能被子类重写修改其逻辑 +// 如果启用了COMPACT_STRINGS机制,就返回当前对象实际存储的编码格式(通过成员变量coder表示) +// 如果未启用COMPACT_STRINGS机制,则统一返回UTF16编码格式,这可能是一种默认或者通用的返回策略 +final byte getCoder() { + return COMPACT_STRINGS? coder : UTF16; +} + +// isLatin1方法用于判断当前对象是否采用的是LATIN1编码格式,它同样是final方法,不可被子类重写 +// 只有在启用了COMPACT_STRINGS机制并且当前对象的编码格式(coder)明确为LATIN1时,才返回true,否则返回false +final boolean isLatin1() { + return COMPACT_STRINGS && coder == LATIN1; +} + +// putCharsAt方法(接收字符数组参数)用于将给定的字符数组中指定范围的字符插入到当前对象内部存储字符的字节数组中指定的索引位置处,并且会根据当前对象的编码格式来进行相应处理 +// 参数index表示在当前对象字节数组中插入字符的目标索引位置,也就是要将给定字符数组中的字符插入到当前对象字节数组的哪个位置开始 +// 参数s表示要插入的字符数组,包含了要插入的字符内容 +// 参数off表示在字符数组s中要插入字符范围的起始索引,确定从字符数组的哪个位置开始取字符进行插入操作 +// 参数end表示要插入字符范围的结束索引,界定了要插入的字符在字符数组中的范围 +private final void putCharsAt(int index, char[] s, int off, int end) { + if (isLatin1()) { + // 如果当前对象采用的是LATIN1编码格式,获取当前对象内部存储字符的字节数组(val),用于后续往里面插入字符 + byte[] val = this.value; + // 通过循环遍历要插入的字符数组(s)中指定范围的字符,i从off开始,j从index开始,i用于遍历字符数组s,j用于定位在当前对象字节数组val中的插入位置 + for (int i = off, j = index; i < end; i++) { + char c = s[i]; + // 判断当前字符(c)是否能够用LATIN1编码表示,通过调用StringLatin1.canEncode方法进行判断 + if (StringLatin1.canEncode(c)) { + // 如果该字符可以用LATIN1编码表示,将字符(c)转换为字节(通过强制类型转换为byte类型)后存储到当前对象字节数组(val)中对应索引(j)位置处,然后更新索引(j++),以便下一个字符能插入到正确的下一个位置 + val[j++] = (byte)c; + } else { + // 如果当前字符不能用LATIN1编码表示,说明需要转换编码格式来存储该字符 + // 先调用inflate方法将当前对象的编码格式转换为UTF16(通常UTF16能处理更广泛的字符情况) + inflate(); + // 调用StringUTF16.putCharsSB方法,将剩余要插入的字符(从当前位置i开始到end索引结束的字符)按照UTF16编码格式插入到当前对象字节数组(this.value)中从索引j位置开始的地方,完成插入操作后直接返回,因为后续的字符已经通过这个方法处理了,不需要再继续循环遍历 + StringUTF16.putCharsSB(this.value, j, s, i, end); + return; + } + } + } else { + // 如果当前对象不是LATIN1编码格式(大概率是UTF16编码格式),直接调用StringUTF16类的putCharsSB方法,将给定字符数组(s)中指定范围的字符按照UTF16编码格式插入到当前对象字节数组(this.value)中从索引index位置开始的地方,进行相应的插入操作 + StringUTF16.putCharsSB(this.value, index, s, off, end); + } +} + +// putCharsAt方法(接收CharSequence参数)用于将给定的CharSequence对象中指定范围的字符插入到当前对象内部存储字符的字节数组中指定的索引位置处,同样会依据当前对象的编码格式来做不同处理 +// 参数index表示在当前对象字节数组中插入字符的目标索引位置 +// 参数s表示要插入的CharSequence对象,它是一个字符序列的抽象表示,像String、StringBuilder等都实现了该接口,这里可以传入不同类型的字符序列对象 +// 参数off表示在CharSequence对象s中要插入字符范围的起始索引 +// 参数end表示要插入字符范围的结束索引 +private final void putCharsAt(int index, CharSequence s, int off, int end) { + if (isLatin1()) { + // 如果当前对象采用的是LATIN1编码格式,获取当前对象内部存储字符的字节数组(val),用于后续插入字符操作 + byte[] val = this.value; + // 通过循环遍历要插入的CharSequence对象(s)中指定范围的字符,i从off开始,j从index开始,i用于遍历CharSequence对象s,j用于定位在当前对象字节数组val中的插入位置 + for (int i = off, j = index; i < end; i++) { + char c = s.charAt(i); + // 判断当前字符(c)是否能够用LATIN1编码表示,调用StringLatin1.canEncode方法进行判断 + if (StringLatin1.canEncode(c)) { + // 如果该字符可以用LATIN1编码表示,将字符(c)转换为字节(通过强制类型转换为byte类型)后存储到当前对象字节数组(val)中对应索引(j)位置处,然后更新索引(j++),以便下一个字符能插入到正确的下一个位置 + val[j++] = (byte)c; + } else { + // 如果当前字符不能用LATIN1编码表示,说明需要转换编码格式来存储该字符 + // 先调用inflate方法将当前对象的编码格式转换为UTF16(通常UTF16能处理更广泛的字符情况) + inflate(); + // 调用StringUTF16.putChar方法将当前字符(c)按照UTF16编码格式存储到当前对象字节数组(this.value)中索引j位置处(确保字节数组中有一个UTF16编码的字符) + // 然后将索引i和j都更新(i++和j++),因为已经处理了当前字符,并且下一个字符插入位置也要相应后移 + // 再调用StringUTF16.putCharsSB方法,将剩余要插入的字符(从当前位置i开始到end索引结束的字符)按照UTF16编码格式插入到当前对象字节数组(this.value)中从索引j位置开始的地方,完成插入操作后直接返回,不再继续循环 + StringUTF16.putChar(this.value, j++, c); + i++; + StringUTF16.putCharsSB(this.value, j, s, i, end); + return; + } + } + } else { + // 如果当前对象不是LATIN1编码格式(大概率是UTF16编码格式),直接调用StringUTF16类的putCharsSB方法,将给定CharSequence对象(s)中指定范围的字符按照UTF16编码格式插入到当前对象字节数组(this.value)中从索引index位置开始的地方,进行相应的插入操作 + StringUTF16.putCharsSB(this.value, index, s, off, end); + } +} + +// inflateIfNeededFor方法(针对String类型参数)用于判断是否需要根据传入的字符串的编码格式来对当前对象进行编码格式转换(将当前对象从可能的LATIN1编码格式转换为UTF16编码格式) +// 参数input表示要与之比较编码格式的字符串,根据当前对象与该传入字符串的编码格式差异以及COMPACT_STRINGS机制是否启用,来决定是否进行转换操作 +private void inflateIfNeededFor(String input) { + if (COMPACT_STRINGS && (coder!= input.coder())) { + // 如果启用了COMPACT_STRINGS机制,并且当前对象的编码格式(coder)与传入字符串(input)的编码格式不同,调用inflate方法进行编码格式转换,将当前对象转换为更合适的编码格式(比如转换为UTF16来适应更多字符情况) + inflate(); + } +} +// inflateIfNeededFor方法(针对AbstractStringBuilder类型参数) +// 该方法用于判断当前对象是否需要根据传入的AbstractStringBuilder对象(input)的编码格式进行编码格式转换(例如从LATIN1转换为UTF16),转换操作是基于COMPACT_STRINGS机制以及两者编码格式是否不同来决定的。 +// 参数input表示传入的AbstractStringBuilder对象,通过比较它与当前对象的编码格式来确定是否需要转换当前对象的编码格式。 +private void inflateIfNeededFor(AbstractStringBuilder input) { + // 判断COMPACT_STRINGS机制是否启用(这通常是一个全局配置相关的标识,用于控制一些优化或特定功能的执行条件),并且当前对象的编码格式(coder)与传入的AbstractStringBuilder对象(input)获取到的编码格式(通过input.getCoder()方法获取)是否不一致。 + if (COMPACT_STRINGS && (coder!= input.getCoder())) { + // 如果满足上述条件,调用inflate方法进行编码格式转换,将当前对象的编码格式转换为更合适的格式,以适应后续可能涉及的操作(比如存储更广泛的字符等情况)。 + inflate(); + } +} + +// putStringAt方法(接收多个参数) +// 此方法的作用是将给定字符串(str)中指定范围(从off索引开始到end索引结束)的字符按照合适的编码格式存储到当前对象内部存储字符的字节数组(value)中指定的索引(index)位置处,在存储前会先根据需要进行编码格式的转换判断。 +// 参数index表示在当前对象字节数组中存储字符的目标索引位置,确定了要将字符串中的字符存储到当前对象字节数组的具体起始位置。 +// 参数str表示要存储字符的字符串,是字符的来源。 +// 参数off表示在字符串str中要存储字符范围的起始索引,用于界定从字符串的哪个位置开始取字符进行存储操作。 +// 参数end表示要存储字符范围的结束索引,明确了要存储的字符串中字符的范围边界。 +private void putStringAt(int index, String str, int off, int end) { + // 先调用inflateIfNeededFor方法,判断当前对象是否需要根据传入的字符串(str)的编码格式进行编码格式转换,如果需要则会在该方法内完成转换操作,确保后续存储操作能在正确的编码格式下进行。 + inflateIfNeededFor(str); + // 调用字符串(str)的getBytes方法,按照当前对象的编码格式(coder)将字符串中指定范围的字符存储到当前对象内部存储字符的字节数组(value)中合适的位置(从off索引和index位置开始存储,存储的字符数量为end - off个),进行实际的字符存储操作,该方法内部会依据编码格式相关逻辑将字符准确存入字节数组中相应位置。 + str.getBytes(value, off, index, coder, end - off); +} + +// putStringAt方法(接收两个参数) +// 功能与上面接收多个参数的putStringAt方法类似,不过这个方法是将整个给定的字符串(str)按照合适的编码格式存储到当前对象内部存储字符的字节数组(value)中指定的索引(index)位置处,同样会先判断是否需要进行编码格式转换。 +// 参数index表示在当前对象字节数组中存储字符的目标索引位置,指定了存储的起始位置。 +// 参数str表示要存储的字符串,包含了需要存入当前对象字节数组的全部字符内容。 +private void putStringAt(int index, String str) { + // 首先调用inflateIfNeededFor方法,判断当前对象是否需要依据传入字符串(str)的编码格式进行编码格式转换,满足条件则进行转换,确保后续存储时编码格式匹配。 + inflateIfNeededFor(str); + // 调用字符串(str)的getBytes方法,按照当前对象的编码格式(coder)将字符串的字符存储到当前对象内部存储字符的字节数组(value)中从index位置开始的地方,进行实际的字符存储操作,使字符串的内容能正确存入字节数组相应位置。 + str.getBytes(value, index, coder); +} + +// appendChars方法(接收字符数组参数) +// 用于将给定的字符数组(s)中指定范围(从off索引开始到end索引结束)的字符追加到当前对象内部存储字符的字节数组(value)末尾,并且会根据当前对象的编码格式情况进行相应处理,处理完后还会更新当前对象中已存储的有效字符数量(count)。 +// 参数s表示要追加的字符数组,包含了要追加到当前对象字节数组的字符内容。 +// 参数off表示字符数组s中要追加字符范围的起始索引,确定了从字符数组的哪个位置开始取字符进行追加操作。 +// 参数end表示要追加字符范围的结束索引,界定了要追加的字符在字符数组中的范围边界。 +private final void appendChars(char[] s, int off, int end) { + int count = this.count; + if (isLatin1()) { + byte[] val = this.value; + // 通过循环遍历要追加的字符数组(s)中指定范围的字符,i从off开始,j从当前已存储的有效字符数量(count)开始,i用于遍历字符数组s,j用于定位在当前对象字节数组val中的追加位置。 + for (int i = off, j = count; i < end; i++) { + char c = s[i]; + if (StringLatin1.canEncode(c)) { + // 如果当前字符(c)能够用LATIN1编码表示,将字符(c)转换为字节(通过强制类型转换为byte类型)后存储到当前对象字节数组(val)中对应索引(j)位置处,然后更新索引(j++),以便下一个字符能追加到正确的下一个位置。 + val[j++] = (byte)c; + } else { + // 如果当前字符不能用LATIN1编码表示,先更新当前对象中已存储的有效字符数量为当前处理到的索引位置(j),表示已经处理到这个位置了,后续追加操作要从这里开始算。 + this.count = count = j; + // 调用inflate方法将当前对象的编码格式转换为UTF16(因为遇到了LATIN1编码无法处理的字符,而UTF16能处理更广泛的字符情况)。 + inflate(); + // 调用StringUTF16.putCharsSB方法,将剩余要追加的字符(从当前位置i开始到end索引结束的字符)按照UTF16编码格式追加到当前对象字节数组(this.value)中从索引j位置开始的地方,完成追加操作后,更新当前对象中已存储的有效字符数量为追加操作完成后的最终数量(count + end - i),并返回,不再继续循环,因为后续字符已经通过这个方法处理完了。 + StringUTF16.putCharsSB(this.value, j, s, i, end); + this.count = count + end - i; + return; + } + } + } else { + // 如果当前对象不是LATIN1编码格式(大概率是UTF16编码格式),直接调用StringUTF16类的putCharsSB方法,将给定字符数组(s)中指定范围的字符按照UTF16编码格式追加到当前对象字节数组(this.value)中从当前已有字符数量(count)位置开始的地方,进行相应的追加操作。 + StringUTF16.putCharsSB(this.value, count, s, off, end); + } + // 更新当前对象中已存储的有效字符数量为追加操作完成后的最终数量(count + end - off),以准确反映追加操作后的字符数量变化情况。 + this.count = count + end - off; +} + +// appendChars方法(接收String类型参数) +// 该方法的作用是将给定的字符串(s)中指定范围(从off索引开始到end索引结束)的字符追加到当前对象内部存储字符的字节数组(value)末尾,会根据当前对象以及给定字符串的编码格式情况进行相应处理,并更新当前对象中已存储的有效字符数量(count)。 +// 参数s表示要追加的字符串,包含了要追加的字符内容。 +// 参数off表示在字符串s中要追加字符范围的起始索引,确定从字符串的哪个位置开始取字符进行追加操作。 +// 参数end表示要追加字符范围的结束索引,界定了要追加的字符在字符串中的范围边界。 +private final void appendChars(String s, int off, int end) { + if (isLatin1()) { + if (s.isLatin1()) { + // 如果当前对象采用的是LATIN1编码格式,并且要追加的字符串(s)也采用的是LATIN1编码格式,使用System.arraycopy方法将字符串(s)中指定范围的字符直接复制到当前对象内部存储字符的字节数组(this.value)中从当前已有字符数量(this.count)位置开始的地方,进行字符追加操作,这样能高效地完成相同编码格式下的字符追加。 + System.arraycopy(s.value(), off, this.value, this.count, end - off); + } else { + // 如果当前对象采用的是LATIN1编码格式,但要追加的字符串(s)不是LATIN1编码格式,先获取当前对象内部存储字符的字节数组(val),然后通过循环遍历要追加的字符串(s)中指定范围的字符进行处理。 + byte[] val = this.value; + for (int i = off, j = count; i < end; i++) { + char c = s.charAt(i); + if (StringLatin1.canEncode(c)) { + // 如果当前字符(c)能够用LATIN1编码表示,将字符(c)转换为字节(通过强制类型转换为byte类型)后存储到当前对象字节数组(val)中对应索引(j)位置处,然后更新索引(j++),以便下一个字符能追加到正确的下一个位置。 + val[j++] = (byte) c; + } else { + // 如果当前字符不能用LATIN1编码表示,先更新索引(count = j),表示已经处理到这个位置了,后续追加操作要从这里开始算。 + count = j; + // 调用inflate方法将当前对象的编码格式转换为UTF16(因为遇到了LATIN1编码无法处理的字符,而UTF16能处理更广泛的字符情况)。 + inflate(); + // 使用System.arraycopy方法将剩余要追加的字符(从当前位置i开始到end索引结束的字符)按照UTF16编码格式追加到当前对象字节数组(this.value)中从索引j << UTF16位置开始的地方(这里的左移操作与UTF16编码格式相关,用于准确确定在字节数组中的位置),完成追加操作后更新索引(count += end - i),表示字符数量的更新,然后返回,不再继续循环,因为后续字符已经通过这个方法处理完了。 + System.arraycopy(s.value(), i << UTF16, this.value, j << UTF16, (end - i) << UTF16); + count += end - i; + return; + } + } + } + } else if (s.isLatin1()) { + // 如果当前对象不是LATIN1编码格式(比如是UTF16编码格式),但要追加的字符串(s)是LATIN1编码格式,调用StringUTF16.putCharsSB方法,将给定字符串(s)中指定范围的字符按照UTF16编码格式追加到当前对象字节数组(this.value)中从当前已有字符数量(this.count)位置开始的地方,进行相应的追加操作,会在方法内部完成编码转换等处理。 + StringUTF16.putCharsSB(this.value, this.count, s, off, end); + } else { // both UTF16 + // 如果当前对象和要追加的字符串(s)都采用的是UTF16编码格式,使用System.arraycopy方法将字符串(s)中指定范围的字符按照UTF16编码格式直接复制到当前对象内部存储字符的字节数组(this.value)中从当前已有字符数量(this.count)位置开始的地方,进行高效的字符追加操作,利用相同编码格式的便利进行复制追加。 + System.arraycopy(s.value(), off << UTF16, this.value, this.count << UTF16, (end - off) << UTF16); + } + // 更新当前对象中已存储的有效字符数量为追加操作完成后的最终数量(count += end - off),准确反映追加操作后的字符数量变化情况。 + count += end - off; +} + +// appendChars方法(接收CharSequence参数) +// 用于将给定的CharSequence对象(s)中指定范围(从off索引开始到end索引结束)的字符追加到当前对象内部存储字符的字节数组(value)末尾,会依据当前对象的编码格式情况进行相应处理,处理完后更新当前对象中已存储的有效字符数量(count)。 +// 参数s表示要追加的CharSequence对象,它是一个字符序列的抽象表示,像String、StringBuilder等都实现了该接口,这里可以传入不同类型的字符序列对象,包含了要追加的字符内容。 +// 参数off表示在CharSequence对象s中要追加字符范围的起始索引,确定从该对象的哪个位置开始取字符进行追加操作。 +// 参数end表示要追加字符范围的结束索引,界定了要追加的字符在CharSequence对象中的范围边界。 +private final void appendChars(CharSequence s, int off, int end) { + if (isLatin1()) { + byte[] val = this.value; + // 通过循环遍历要追加的CharSequence对象(s)中指定范围的字符,i从off开始,j从当前已存储的有效字符数量(count)开始,i用于遍历CharSequence对象s,j用于定位在当前对象字节数组val中的追加位置。 + for (int i = off, j = count; i < end; i++) { + char c = s.charAt(i); + if (StringLatin1.canEncode(c)) { + // 如果当前字符(c)能够用LATIN1编码表示,将字符(c)转换为字节(通过强制类型转换为byte类型)后存储到当前对象字节数组(val)中对应索引(j)位置处,然后更新索引(j++),以便下一个字符能追加到正确的下一个位置。 + val[j++] = (byte)c; + } else { + // 如果当前字符不能用LATIN1编码表示,先更新索引(count = j),表示已经处理到这个位置了,后续追加操作要从这里开始算。 + count = j; + // 调用inflate方法将当前对象的编码格式转换为UTF16(因为遇到了LATIN1编码无法处理的字符,而UTF16能处理更广泛的字符情况)。 + inflate(); + // 调用StringUTF16.putChar方法将当前字符(c)按照UTF16编码格式存储到当前对象字节数组(this.value)中索引j++位置处(确保字节数组中有一个UTF16编码的字符),然后再次更新索引(count = j),接着将索引i++(因为已经处理了当前字符,下一个字符位置要后移),再调用StringUTF16.putCharsSB方法,将剩余要追加的字符(从当前位置i开始到end索引结束的字符)按照UTF16编码格式追加到当前对象字节数组(this.value)中从索引j位置开始的地方,完成追加操作后更新索引(count += end - i),表示字符数量的更新,然后返回,不再继续循环,因为后续字符已经通过这个方法处理完了。 + StringUTF16.putChar(this.value, j++, c); + count = j; + i++; + StringUTF16.putCharsSB(this.value, j, s, i, end); + count += end - i; + return; + } + } + } else { + // 如果当前对象不是LATIN1编码格式(大概率是UTF16编码格式),直接调用StringUTF16类的putCharsSB方法,将给定CharSequence对象(s)中指定范围的字符按照UTF16编码格式追加到当前对象字节数组(this.value)中从当前已有字符数量(count)位置开始的地方,进行相应的追加操作。 + StringUTF16.putCharsSB(this.value, count, s, off, end); + } + // 更新当前对象中已存储的有效字符数量为追加操作完成后的最终数量(count += end - off),以准确反映追加操作后的字符数量变化情况。 + count += end - off; +} \ No newline at end of file diff --git a/szgz-branch1/运行结果.png b/szgz-branch1/运行结果.png new file mode 100644 index 0000000..62f44da Binary files /dev/null and b/szgz-branch1/运行结果.png differ