String 类中一个神奇的构造方法


前言

private final char value[];

java 中 String 是不可变字符串,通过源码可以知晓原因。

字符串本质是通过字符数组存储的,并且声明为了 final,因此在第一次赋值后就不能进行更改了

注:char value[]等同于char[] value,这是 java 为了c++程序员设计的用来适应的语法

神奇的构造器

String(char[] value, boolean share) {
    // assert share : "unshared not supported";
    this.value = value;
}

这个构造器是唯一一个不被public修饰的,不能被我们在外部调用。同时,与其他构造器的最大区别是:这里直接修改value的地址引用,而其他构造器都是将参数拷贝一份作为value

不加访问权限修饰符,默认为default,也就是同包下可以访问,借助IDEA总共可以找到9处引用

image-20211115205555141

方便起见,直接分析String类中的引用

concat

public String concat(String str) {
    int otherLen = str.length();
    if (otherLen == 0) {
        return this;
    }
    int len = value.length;
    char buf[] = Arrays.copyOf(value, len + otherLen);
    str.getChars(buf, len);
    return new String(buf, true);
}

concat 方法用来拼接字符串,并且返回拼接后的新字符串。

拼接的源码部分不难理解。

而最后生成字符串用的构造器正是上面介绍的这个构造器。

理解

  • 直接修改value引用的好处是,减少了重新拷贝一次的性能损失。

  • 不能随意使用这个构造器,原因是新生成的字符串如果和另一个变量共享一个地址,那么当这个变量的值修改时,字符串也会修改,如下例子:

    static class Test {
        private final int[] value;
        public Test(int[] value) {
            this.value = value;
        }
        @Override
        public String toString() {
            return Arrays.toString(value);
        }
    }
    public static void main(String[] args) {
        int[] ints = new int[]{1, 2, 3};
        Test test = new Test(ints);
        System.out.println(test);
        ints[0] = 0;
        System.out.println(test);
    }

    运行效果如下:

    [1, 2, 3]
    [0, 2, 3]
    
    Process finished with exit code 0

    所以使用这个构造器的前提是:作为参数的那个变量,将不再使用(或者说即将被销毁)

  • concat 正好是要生成一个新的字符串,并且在具体的代码实现中使用了 char 数组,当方法结束时,这个数组已经被新字符串引用,而原本的变量 buf也在方法结束时销毁了,符合了上述条件,也提高了性能

总结

String(char[] value, boolean share) {
    // assert share : "unshared not supported";
    this.value = value;
}
  • 这个构造器被 JDK 用来提高字符串创建的性能
  • 使用这个构造器有前提,否则会造成字符串可变
  • 开发中无法调用这个构造器,但是可以学习这种设计

文章作者: ❤纱雾
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 ❤纱雾 !
评论
  目录