Java源码阅读计划(1) String<II>

String的构造函数

String有很多构造函数,不同种类的有不同的作用,接下来我们一个个看下去

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

初始化一个String对象,使其表示一个空字符串序列,由于字符串的不可变性,这个方法其实是不必要的。

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

简单易懂,除非需要显式复制,否则这个方法也是不必要,因为字符串的不可变性。

  • 3
    public String(char value[]) {
        this.value = Arrays.copyOf(value, value.length);
    }

依然很简单,不过要注意字符数组的后续修改不会影响新创建的字符串。

  • 4
    public String(char value[], int offset, int count) {
        if (offset < 0) {
            throw new StringIndexOutOfBoundsException(offset);
        }
        if (count <= 0) {
            if (count < 0) {
                throw new StringIndexOutOfBoundsException(count);
            }
            if (offset <= value.length) {
                this.value = "".value;
                return;
            }
        }
        // Note: offset or count might be near -1>>>1.
        if (offset > value.length - count) {
            throw new StringIndexOutOfBoundsException(offset + count);
        }
        this.value = Arrays.copyOfRange(value, offset, offset+count);
    }

这个方法是从字符数组中截取一段作为value的值,这里面做了一些关于长度与角标的判断,另外,同样的,这里形参char[]的改变与其之前所创建的String对象已经没有关系了

  • 5
   public String(int[] codePoints, int offset, int count) {
        if (offset < 0) { //简单的校验
            throw new StringIndexOutOfBoundsException(offset);
        }
        if (count <= 0) {
            if (count < 0) {  //简单的校验
                throw new StringIndexOutOfBoundsException(count);
            }
            if (offset <= codePoints.length) { //如果截取起点大于int数组长度,那么返回空字符串
                this.value = "".value;
                return;
            }
        }
        // Note: offset or count might be near -1>>>1.
        if (offset > codePoints.length - count) { //简单的判断
            throw new StringIndexOutOfBoundsException(offset + count);
        }

        final int end = offset + count; //获取终点位置

        // Pass 1: Compute precise size of char[]
        int n = count; //由于Unicode的编码特点,字符串的真实长度并不是int数组的长度,用n这个变量来记录字符串的真实长度,下面的for循环就是在计算真正的长度
        for (int i = offset; i < end; i++) {
            int c = codePoints[i];
            if (Character.isBmpCodePoint(c))//一个码元长度
                continue;
            else if (Character.isValidCodePoint(c))//两个码元长度,需要增加字符串长度
                n++;
            else throw new IllegalArgumentException(Integer.toString(c));
        }

        // Pass 2: Allocate and fill in char[]
        final char[] v = new char[n];

        for (int i = offset, j = 0; i < end; i++, j++) {
            int c = codePoints[i];
            if (Character.isBmpCodePoint(c))
                v[j] = (char)c;
            else
                Character.toSurrogates(c, v, j++);
        }

        this.value = v;
    }

这个方法涉及到Unicode编码的知识:
传入int作为参数,这个int是这个字对应的Unicode(16进制数)。每个最大65535 0xFFFF

public static final int MIN_CODE_POINT = 0x000000;

public static final int MAX_CODE_POINT = 0X10FFFF;

UTF-16中的基本单位是两个字节的码元,基本的码元范围是(0x0000-0xFFFF), UTF-16的字符映射范围是(U+0000,U+10FFFF),
当一个生僻字符需要使用0xFFFF以上的映射范围时,其需要使用两个码元(4Byte)进行表示. 其映射规则如下
第一个码元(前导代理)范围:0xD800 - 0xDBFF
第二个码元(后尾代理)范围:0xDC00 - 0xDFFF
有:(0xDBFF-0xD800+1)*(0xDFFF-0xDC00+1) === (0x10FFFF-0xFFFF)双射
所以(0xD800 - 0xDBFF)范围内的码元不能单独表示字符,其必须与后尾代理一起构成一个完整字符.
参考文档:https://www.joelonsoftware.com/2003/10/08/the-absolute-minimum-every-software-developer-absolutely-positively-must-know-about-unicode-and-character-sets-no-excuses/

   public String(int[] codePoints, int offset, int count) {
        if (offset < 0) { //简单的校验
            throw new StringIndexOutOfBoundsException(offset);
        }
        if (count <= 0) {
            if (count < 0) {  //简单的校验
                throw new StringIndexOutOfBoundsException(count);
            }
            if (offset <= codePoints.length) { //如果截取起点大于int数组长度,那么返回空字符串
                this.value = "".value;
                return;
            }
        }
        // Note: offset or count might be near -1>>>1.
        if (offset > codePoints.length - count) { //简单的判断
            throw new StringIndexOutOfBoundsException(offset + count);
        }

        final int end = offset + count; //获取终点位置

        // Pass 1: Compute precise size of char[]
        int n = count; //由于Unicode的编码特点,字符串的真实长度并不是int数组的长度,用n这个变量来记录字符串的真实长度,下面的for循环就是在计算真正的长度
        for (int i = offset; i < end; i++) {
            int c = codePoints[i];
            if (Character.isBmpCodePoint(c))//一个码元长度
                continue;
            else if (Character.isValidCodePoint(c))//两个码元长度,需要增加字符串长度
                n++;
            else throw new IllegalArgumentException(Integer.toString(c));
        }

        // Pass 2: Allocate and fill in char[]
        final char[] v = new char[n];

        for (int i = offset, j = 0; i < end; i++, j++) {
            int c = codePoints[i];
            if (Character.isBmpCodePoint(c))
                v[j] = (char)c;
            else
                Character.toSurrogates(c, v, j++);
        }

        this.value = v;
    }

这个方法涉及到Unicode编码的知识:
传入int作为参数,这个int是这个字对应的Unicode(16进制数)。每个最大65535 0xFFFF

public static final int MIN_CODE_POINT = 0x000000;

public static final int MAX_CODE_POINT = 0X10FFFF;

UTF-16中的基本单位是两个字节的码元,基本的码元范围是(0x0000-0xFFFF), UTF-16的字符映射范围是(U+0000,U+10FFFF),
当一个生僻字符需要使用0xFFFF以上的映射范围时,其需要使用两个码元(4Byte)进行表示. 其映射规则如下
第一个码元(前导代理)范围:0xD800 - 0xDBFF
第二个码元(后尾代理)范围:0xDC00 - 0xDFFF
有:(0xDBFF-0xD800+1)*(0xDFFF-0xDC00+1) === (0x10FFFF-0xFFFF)双射
所以(0xD800 - 0xDBFF)范围内的码元不能单独表示字符,其必须与后尾代理一起构成一个完整字符.
参考文档:https://www.joelonsoftware.com/2003/10/08/the-absolute-minimum-every-software-developer-absolutely-positively-must-know-about-unicode-and-character-sets-no-excuses/

  • 6
    public String(StringBuilder builder) {
        this.value = Arrays.copyOf(builder.getValue(), builder.length());
    }

    public String(StringBuffer buffer) {
        synchronized(buffer) {
            this.value = Arrays.copyOf(buffer.getValue(), buffer.length());
        }
    }

这里也体现了StringBuilder和StringBuffer的并发安全性。

原文地址:https://www.cnblogs.com/figsprite/p/11795280.html