JAVA源码浅析 01 - String构造方法浅析

解决问题

  1. 知道String是如何实现
  2. String的相关构造方法以及实现

基本概念

  1. String类被final关键字修饰,String类不能被继承,所有成员方法都默认为final方法;
  2. 实现了Serializable、CharSequence、 Comparable接口。
  3. 内部实现字符数组。

内部实现 - 使用Char数组

public final class String
    implements java.io.Serializable, Comparable<String>, CharSequence {
    /** The value is used for character storage. */
    // 缓存字符串的值
    private final char value[];

    /** Cache the hash code for the string */
    // 缓存 HASH 值
    private int hash; // Default to 0

    /** use serialVersionUID from JDK 1.0.2 for interoperability */
    private static final long serialVersionUID = -6849794470754667710L;

JDK1.8的String内部是实现了一个char数组来缓存字符串的值,如上代码。

个别重要的构造方法

1 - 普通构造

深拷贝了一个String对象,将入参的value,hash进行copy。

    // 普通构造, 参数就是String
    public String(String original) {
        this.value = original.value;
        this.hash = original.hash;
    }

2 - char[]字符数组构造

通过 Arrays.copyOf 方法拷贝字符数组,去构建字符串


    // 将 char数组作为参数
    public String(char value[]) {
        this.value = Arrays.copyOf(value, value.length);
    }

还是通过字符数组来构建一个字符串,不过加入了初始下标offset与字符偏移量count来进行截取构建。

    
    // 将char数组作为参数, 
    // offset表示第一个被截取的字符在数组value[]中的下标
    // count表示从此字符开始向后截取的字符的数量
    // 将value[]数组按照传入的下标和指定的截取数组数据的数量进行截取
    
    public String(char value[], int offset, int count) {
        // 下标小于0则越界
        if (offset < 0) {
            throw new StringIndexOutOfBoundsException(offset);
        }
        // 向后截取数量长度≤0越界
        if (count <= 0) {
            if (count < 0) {
                throw new StringIndexOutOfBoundsException(count);
            }
            // 如果符合字符数据的长度内, 同时count=0
            // 那么就是数组的原来长度,不用截取
            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);
    }

3 - bytes[]字节构造

编码一个字节数组,offset用来设定初始下标,再截取length个长度来构建字符串,charsetName是字符编码的格式

    
    // 同上
    // 加入了参数 charsetName,指定字符编码
    public String(byte bytes[], int offset, int length, String charsetName)
            throws UnsupportedEncodingException {
        if (charsetName == null)
            throw new NullPointerException("charsetName");
        // 检查bytes数组合法性
        checkBounds(bytes, offset, length);
        this.value = StringCoding.decode(charsetName, bytes, offset, length);
    }

构建思路同上,字符编码格式参数由 String 换成 Charset


    // 同上
    public String(byte bytes[], int offset, int length, Charset charset) {
        if (charset == null)
            throw new NullPointerException("charset");
        checkBounds(bytes, offset, length);
        this.value =  StringCoding.decode(charset, bytes, offset, length);
    }

省略了charsetName字符编码格式参数,使用系统默认编码。

    
    // 将字节作为参数,将bytes解码成字符数组
    // offset 要解码的第一个 byte 的下标
    // length 要解码的 byte 数 的长度
    public String(byte bytes[], int offset, int length) {
        // 检查bytes数组合法性
        checkBounds(bytes, offset, length);
        this.value = StringCoding.decode(bytes, offset, length);
    }

这两种构造方法不进行指定长度截取,只按照字符编码格式进行编码

    // String编码格式
     public String(byte bytes[], String charsetName)
            throws UnsupportedEncodingException {
        this(bytes, 0, bytes.length, charsetName);
    }
    // Charset
    public String(byte bytes[], Charset charset) {
    this(bytes, 0, bytes.length, charset);
    }

默认系统编码格式,不截取bytes数组构建字符串

    // 将字节作为参数
    public String(byte bytes[]) {
        this(bytes, 0, bytes.length);
    }

4 - String Buffer构造

同一时间只允许一个线程对这个 buffer 构建成String对象,所以是线程安全的。

    // 将StringBuffer 作为参数
    // 这里用到了 synchronized 关键字,线程安全
    public String(StringBuffer buffer) {
        synchronized(buffer) {
            this.value = Arrays.copyOf(buffer.getValue(), buffer.length());
        }
    }

5 - String build构造

将StringBuilder通过Arrays.copyOf构建一个字符串,没有使用synchronized修饰,所以是非线程安全的。

    
    // 将StringBuilder作为参数
    public String(StringBuilder builder) {
        this.value = Arrays.copyOf(builder.getValue(), builder.length());
    }

总结

String类有多种构造方法,如下几个较为主要,建议常记:

  • 通过深拷贝一个String对象。
  • 通过字符数组进行构建。
  • 通过字符数组构建,并设置起始下标与偏移量截取。
  • 通过字节数组构建,并设置起始下标与偏移量截取,可以指定字节编码格式,未指定就默认系统编码格式。
  • 通过StringBuffer构建,用synchronized修饰,是线程安全的。
  • 通过StringBuilder构建,是非线程安全的。

下期文章继续深入分析String类。

发布了104 篇原创文章 · 获赞 264 · 访问量 54万+

猜你喜欢

转载自blog.csdn.net/teavamc/article/details/105010917