java源码分析---String类(JDK14)


更多源码分析,请点击


String

String 类被 final 修饰,不可以被继承,实现了 SerializableComparableCharSequenceConstableConstantDesc 接口。

String不可变字符序列,创建后值不可以更改。支持字符串连接符 “ + ”。同时可以根据索引值访问其中的字符,索引值是指 char 代码单位。在阅读本文前,应先了解代码点和代码单元的相关内容,点此了解详情

public final class String implements java.io.Serializable, Comparable<String>, CharSequence,
               Constable, ConstantDesc {
   

    @Stable
    private final byte[] value;
    private final byte coder;
    private int hash;
    private boolean hashIsZero; // Default to false;
    @java.io.Serial
    private static final long serialVersionUID = -6849794470754667710L;
    static final boolean COMPACT_STRINGS;
    static {
   
        COMPACT_STRINGS = true;
    }
    private static final ObjectStreamField[] serialPersistentFields = new ObjectStreamField[0];
}

value 用来存放字符,采用 byte[] 存放,并且被 final 修饰,不可以被修改。***@Stable*** 注释表示信任 value 数组的内容。

coder 表示 value 数组的编码的id 0 为LATIN1编码 1 为UTF16编码,因为 value 不可以被修改,所以 coder 也被 final 修饰。

hash 缓存字符串的哈希值,默认为0。

hashIsZero 表示如果哈希已被计算为实际上为零,则进行缓存,这使我们避免重新计算该哈希。默认为false。

COMPACT_STRINGS 表示该字符串是否可以被压缩,用于具有多个可能实现路径的方法。在这里被初始化为 true ,表示默认采用 LATIN1 编码方式。

Latin1是ISO-8859-1的别名,有些环境下写作Latin-1。ISO-8859-1编码是单字节编码,向下兼容ASCII,其编码范围是0x00-0xFF,0x00-0x7F之间完全和ASCII一致,0x80-0x9F之间是控制字符,0xA0-0xFF之间是文字符号。

ISO-8859-1收录的字符除ASCII收录的字符外,还包括西欧语言、希腊语、泰语、阿拉伯语、希伯来语对应的文字符号。ASCII编码是一个7位的容器,ISO-8859-1编码是一个8位的容器。

serialPersistentFields String类在字符串序列化中是特殊情况。

构造方法

String()

创建一个 String 对象,初始化为空的字符序列。因为 String 对象是不可变字符序列,所以一般不使用这个构造器。

public String() {
   
    this.value = "".value;
    this.coder = "".coder;
}

String(String)

创建一个 String 对象,初始化为和给定参数 origianl 一样,即将给定的字符串拷贝了一份。

public String(String original) {
   
    this.value = original.value;
    this.coder = original.coder;
    this.hash = original.hash;
}

String(char[])

创建一个 String 对象,初始化内容为字符数组中的字符。调用本类中的其他构造方法实现,点此查看

public String(char value[]) {
   
    this(value, 0, value.length, null);
}

String(char[], int, int)

创建一个 String 对象,初始化内容为字符数组中从索引 offset 开始的 count 个字符。调用本类中的其他构造方法实现,点此查看

调用 rangeCheck 方法判断是否会发生数组索引越界,即从索引 offset 开始并没有 count 个元素,或者 offsetcount 小于零,则抛出 StringIndexOutOfBoundsException 异常。

public String(char value[], int offset, int count) {
   
    this(value, offset, count, rangeCheck(value, offset, count));
}

rangeCheck(char[], int, int)

检查给定的char数组从 offset 位置开始,是否含有 count 个字符。如果没有,抛出索引越界异常。

private static Void rangeCheck(char[] value, int offset, int count) {
   
    checkBoundsOffCount(offset, count, value.length);
    return null;
}

checkBoundsOffCount(int, int, int)

根据字符字符序列长度和所需数量 count 判断字符串索引是否会越界。

static void checkBoundsOffCount(int offset, int count, int length) {
   
    if (offset < 0 || count < 0 || offset > length - count) {
   
        throw new StringIndexOutOfBoundsException(
            "offset " + offset + ", count " + count + ", length " + length);
    }
}

String(char[], int, int, Void)

包私有构造函数,创建一个 String 对象,初始化内容为字符数组 value 中从索引 off 开始的 len 个字符。其中 Void 类型参数用于和其他构造函数进行区分,以便进行重载,和 void 类型含义一致,Void 类不能被实例化。

  • 如果长度 len 为0,则初始化字符串为 “”。
  • 根据 COMPACT_STRINGS 判断当前啊字符串编码
  • 如果是 LATIN1 编码,调用 StringUTF16.compress() 方法(见下文)实现将 value 数组中的字符转为字节数组赋值给 val
  • 如果是 UTF16 编码,调用 StringUTF16.toBytes() 方法(见下文)实现将 value 数组中的字符转为字节数组。
String(char[] value, int off, int len, Void sig) {
   
    if (len == 0) {
   
        this.value = "".value;
        this.coder = "".coder;
        return;
    }
    if (COMPACT_STRINGS) {
   
        byte[] val = StringUTF16.compress(value, off, len);
        if (val != null) {
   
            this.value = val;
            this.coder = LATIN1;
            return;
        }
    }
    this.coder = UTF16;
    this.value = StringUTF16.toBytes(value, off, len);
}
StringUTF16.compress(char[], int, int)

value 数组中从索引 off 处开始的 len 个字符转为字节数组返回。

public static byte[] compress(char[] val, int off, int len) {
   
    byte[] ret = new byte[len];
    if (compress(val, off, ret, 0, len) == len) {
   
        return ret;
    }
    return null;
}
//将字符数组src中从索引srcOff处开始的len个字符转换为字节数组存储到dst中索引dstOff处。 (LATIN1编码)
public static int compress(char[] src, int srcOff, byte[] dst, int dstOff, int len) {
   
    //一共有len个字符,所以循环len次
    for (int i = 0; i < len; i++) {
   
        //逐个取出src中的字符
        char c = src[srcOff];
        //如果c大于0xFF,即字符c不是用LATIN1编码存储,直接返回
        if (c > 0xFF) {
   
            len = 0;
            break;
        }
        //将字符转为byte类型存储到dst数组中
        dst[dstOff] = (byte)c;
        srcOff++;
        dstOff++;
    }
    return len;
}
StringUTF16.toBytes()

将UTF16格式编码的字符数组value转换为字节数组返回。

  • newBytesFor() 方法创建长度为len*2的byte数组,如果len的长度小于0或者大于MAX_LENGTH(Integer.MAX_VALUE << 1)则抛出异常。
  • 循环调用 putChar() 方法将字符逐个转为byte类型存入val数组中。
    • assert,断言,如果后面表达式为真,不采取任何措施,否则,抛出 AssertionError() ,后面的字符串即传入该错误的构造函数的值。
    • 因为是将LATIN1编码存储的字符串转换为UTF16编码形式,所以需要对索引index左移一位。然后将原数组value中的值拷贝到buf数组中。
public static byte[] toBytes(char[] value, int off, int len) {
   
    byte[] val = newBytesFor(len);
    for (int i = 0; i < len; i++) {
   
        putChar(val, i, value[off]);
        off++;
    }
    return val;
}
static void putChar(byte[] val, int index, int c) {
   
    assert index >= 0 && index < length(val) : "Trusted caller missed bounds check";
    index <<= 1;
    val[index++] = (byte)(c >> HI_BYTE_SHIFT); //大小端存储
    val[index]   = (byte)(c >> LO_BYTE_SHIFT);
}

String(byte[])

创建一个 String 对象,采用默认字符集对字节数组 bytes 进行解码,调用其他构造方法实现,具体见下文。

public String(byte[] bytes) {
   
    this(bytes, 0, bytes.length);
}

String(byte[], int, int)

创建一个 String 对象,采用默认字符集对字节数组 bytes 从索引 offset 处开始对 length 字节进行解码。

public String(byte bytes[], int offset, int length) {
   
    //根据数组长度和所需length判断索引是否会越界。
    checkBoundsOffCount(offset, length, bytes.length);
    StringCoding.Result ret = StringCoding.decode(bytes, offset, length);
    this.value = ret.value;
    this.coder = ret.coder;
}

String(byte[], String)

创建一个 String 对象,采用指定字符集对字节数组 bytes 进行解码。调用其他构造方法实现,具体见下文。

public String(byte bytes[], String charsetName) throws UnsupportedEncodingException {
   
    this(bytes, 0, bytes.length, charsetName);
}

String(byte[], int, int, String)

创建一个 String 对象,采用指定字符集对字节数组 bytes 从索引 offset 处开始对 length 字节进行解码。

public String(byte bytes[], int offset, int length, String charsetName)
        throws UnsupportedEncodingException {
   
    if (charsetName == null)
        throw new NullPointerException("charsetName");
    checkBoundsOffCount(offset, length, bytes.length);
    StringCoding.Result ret =
        StringCoding.decode(charsetName, bytes, offset, length);
    this.value = ret.value;
    this.coder = ret.coder;
}

String(byte[], Charset)String(byte[], int, int, Charset) 方法和上述两个方法类似,不再赘述。

String(StringBuffer)

创建一个 String 对象,并初始化为 StringBuffer 对象 buffer 中的字符序列(复制 buffer 中的内容)调用构造方法 String(String) 实现

public String(StringBuffer buffer) {
   
    this(buffer.toString());
}

String(StringBuilder)

创建一个 String 对象,并初始化为 StringBuilder 对象 builder 中的字符序列(复制 builder 中的内容)。

调用构造方法 String(AbstractStringBuilder, Void) 实现。

public String(StringBuilder builder) {
   
    this(builder, null);
}

String(AbstractStringBuilder, Void)

创建一个 String 对象,并初始化为 AbstractStringBuilder 对象 asb 中的字符序列(复制 asb 中的内容)。

其中 Void 类型参数用于和其他构造函数进行区分,以便进行重载,和 void 类型含义一致,Void 类不能被实例化。

  • 如果字符串 asb 采用 LATIN1 编码,直接调用 Arrays.copyOfRange() 方法对字符串进行拷贝。
  • 如果字符串 asb 可以被压缩,调用 StringUTF16.compress(bytet[], int, int) 方法将字符串转为 byte 数组。
  • 否则,设置字符串的编码方式为 UTF16 ,直接调用 Arrays.copyOfRange() 方法对字符串进行拷贝。
String(AbstractStringBuilder asb, Void sig) {
   
    byte[] val = asb.getValue();
    int length = asb.length();
    if (asb.isLatin1()) {
   
        this.coder = LATIN1;
        this.value = Arrays.copyOfRange(val, 0, length);
    } else {
   
        if (COMPACT_STRINGS) {
   
            byte[] buf = StringUTF16.compress(val, 0, length);
            if (buf != null) {
   
                this.coder = LATIN1;
                this.value = buf;
                return;
            }
        }
        this.coder = UTF16;
        this.value = Arrays.copyOfRange(val, 0, length << 1);
    }
}
StringUTF16.compress(byte[], int, int)

将字节数组 val 从索引 off 处开始的 len 个字符转为字节数组返回。

  • 循环调用 getChar 方法逐个取出src中的字符。
  • 如果c大于0xFF,即字符c不是用 LATIN1 编码存储,直接返回。
  • 将字符c转为 byte 类型存入 dst 字节数组中。
public static byte[] compress(byte[] val, int off, int len) {
   
    byte[] ret = new byte[len];
    if (compress(val, off, ret, 0, len) == len) {
   
        return ret;
    }
    return null;
}
//将字符数组src中从索引srcOff处开始的len个字符转换为字节数组存储到dst中索引dstOff处。 (UTF16编码)
public static int compress(byte[] src, int srcOff, byte[] dst, int dstOff, int len) {
   
    // We need a range check here because 'getChar' has no checks
    checkBoundsOffCount(srcOff, len, src);
    for (int i = 0; i < len; i++) {
   
        char c = getChar(src, srcOff);
        if (c > 0xFF) {
   
            len = 0;
            break;
        }
        dst[dstOff] = (byte)c;
        srcOff++;
        dstOff++;
    }
    return len;
}
StringUTF16.getChar(byte[], int)

将字节数组 val 中索引 index 处的值转换为 char 类型的值(两字节)。

static char getChar(byte[] val, int index) {
   
    assert index >= 0 && index < length(val) : "Trusted caller missed bounds check";
    index <<= 1;
    return (char)(((val[index++] & 0xff) << HI_BYTE_SHIFT) |
                  ((val[index]   & 0xff) << LO_BYTE_SHIFT));
}

其他方法

length()

返回该字符串的长度, coder 方法返回字符串的编码方式,当字符串采用 LATIN1 编码时,返回0。返回的字符串长度即为 value 数组的长度。当字符串采用 UTF16 编码时,返回1。返回的字符串长度即为 value 数组的长度的一半(UTF16编码占两个字节)。

public int length() {
   
    return value.length >> coder();
}

coder()

如果 COMPACT_STRINGS 为true,返回coder(0),否则,返回UTF16(1)。

byte coder() {
   
    return COMPACT_STRINGS ? coder : UTF16;
}

isEmpty()

如果字符串长度为零,返回true;否则,返回false。

public boolean isEmpty() {
   
    return value.length == 0;
}

charAt(int)

返回字符串索引 index 处的字符。

isLatin1() 方法判断字符串是否为 LATIN1 编码,根据编码方式的不同选择不同的 charAt 方法实现。

public char charAt(int index) {
   
    if (isLatin1()) {
   
        return StringLatin1.charAt(value, index);
    } else {
   
        return StringUTF16.charAt(value, index);
    }
}
StringLatin1.charAt(byte[], int)

直接将字节数组 value 中索引 index 处的值转换为 char 类型。

public static char charAt(byte[] value, int index) {
   
    if (index < 0 || index >= value.length) {
   
        throw new StringIndexOutOfBoundsException(index);
    }
    return (char)(value[index] & 0xff);
}
StringUTF16.charAt(byte[], int)

先检查索引 index 是否越界,然后调用 StringUTF16.getChar() 方法获取索引 index 处的值。点此查看 getChar 方法详情。

public static char charAt(byte[] value, int index) {
   
    checkIndex(index, value);
    return getChar(value, index);
}

codePointAt(int)

返回当前字符串指定索引处的代码点。关于代码点和代码单元,点此了解详情

isLatin1() 方法判断字符串是否为 LATIN1 编码

  • 如果字符串为 LATIN1 编码,直接返回字节数组 value 索引 index 处的值。
  • 如果字符串为 UTF16 编码,需要将 value 数组长度右移一位,因为 UTF16 编码每个字符占两个字节。调用 StringUTF16.codePointAt() 方法实现。
public int codePointAt(int index) {
   
    if (isLatin1()) {
   
        checkIndex(index, value.length);
        return value[index] & 0xff;
    }
    int length = value.length >> 1;
    checkIndex(index, length);
    return StringUTF16.codePointAt(value, index, length);
}
StringUTF16.codePointAt(byte[], int, int, boolean)

返回字符数组 value 指定索引处的代码点。(UTF16编码)

private static int codePointAt(byte[] value, int index, int end, boolean checked) {
   
    assert index < end;
    if (checked) {
   
        checkIndex(index, value);
    }
    char c1 = getChar(value, index);
    if (Character.isHighSurrogate(c1) && ++index < end) {
   
        if (checked) {
   
            checkIndex(index, value);
        }
        char c2 = getChar(value, index);
        if (Character.isLowSurrogate(c2)) {
   
           return Character.toCodePoint(c1, c2);
        }
    }
    return c1;
}

Character.isHighSurrogate() 和 Character.isLowSurrogate() 函数分别判断给定的char值是否为Unicode高代理代码单元/低代理代码单元。这样的值本身不表示字符串,而是用于UTF-16编码中的补充字符的表示。

High-Surrogate Code Unit. A 16-bit code unit in the range D80016 to DBFF16, used in UTF-16 as the leading code unit of a surrogate pair. Also known as a <

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值