美文网首页
String 源码

String 源码

作者: 西安法律咨询服务平台与程序员 | 来源:发表于2019-04-25 08:58 被阅读0次

本文中的源码源于JDK 8

String类的定义

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

String类被设计成final的,避免被继承。同时String类实现了Serializable、Comparable、CharSequence接口。其中Serializable接口
为标记接口,用来实现序列化和反序列化。【Comparable和CharSequence未完待续】

String类的属性

/** The value is used for character storage. */
private final char value[];
/** Cache the hash code for the string */
private int hash; // Default to 0 

主要有2个属性,final的char数组value和默认值为0的hash。其中char数组value用来存储String对象的字符序列,并且其为final的,
这也就是大家常说的“不可变字符串”的由来;hash用来存储String对象的hash code值。

String类的构造方法

1 无参的构造方法

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

该构造方法是将空串""内部的char数组的引用复制一份,构造出一个空的String对象。该对象与空串""的区别,通过下面的测试用例和对象内存
分析图来说明。

@Test
public void testString() {
    String emptyStr = "";
    String emptyStrCreatedByConstruct = new String();
    Assert.assertFalse(emptyStr == emptyStrCreatedByConstruct);
    Assert.assertTrue(emptyStr.equals(emptyStrCreatedByConstruct));
}
image

通过上面的测试用例和对象内存分析图,可以看出:""对象与用String()构造出的对象不是同一对象;""对象与用String()构造出的对象的内容
是相等的。

2 以String为参数的构造方法

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

该构造方法是将original对象的char数组的引用和hash值复制一份,构造出一个新的String对象。该对象与original对象的区别,通过下面的测
试用例和对象内存分析图来说明。

@Test
public void testString1() {
    String original = "abc";
    String current = new String(original);
    Assert.assertFalse(original == current);
    Assert.assertTrue(original.equals(current));
    System.out.println(original.hashCode());
}  
image

3 以char数组为参数的构造方法

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

该构造方法将char数组value复制一份(而不是将value的引用复制一份),构造出一个新的String的对象。通过下面的测试用例和对象内存分析
图,来展示该构造函数的用法。

@Test
public void testString2 () {
    char[] charArray = {'a','b','c'};
    String str = new String(charArray);
}
image
  • (2)
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);
}

该构造方法将char数组value从offset索引开始拷贝count个char字符(包含offset索引位置),构造出一个新的String对象。通过下面的测试用
例和对象内存分析图展示该构造函数的用法。

@Test
public void testString3 () {
    char[] charArray = {'a','b','c','d'};
    String str = new String(charArray,1,2);
    Assert.assertTrue("bc".equals(str));
}
image

4 以byte数组为参数的构造方法

String构造方法大多以byte数组为参数,这些构造方法可以分为3类:指定字符集;指定字符集名称;不指定字符集。

4.1 指定字符集

  • (1)
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);
}

该构造方法将byte数组bytes从offset索引位置开始(包含offset位置)的length个byte,使用指定的charset字符集进行解码,生成一
个char数组,然后将该char数组的引用赋给新生成String对象的内部的char数组value。

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

该构造方法调用了4.1 (1)中的构造方法,将byte数组bytes,使用指定的charset字符集进行解码,从而生成一个新的String对象。

4.2 指定字符集名称

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

该构造方法类似于4.1 (1)的构造方法,区别仅是4.1 (1)的构造方法的入参为字符集,而该构造方法的入参为字符集的名称。该构造方法将
byte数组从bytes从offset索引位置(包括offset位置)开始,的length个byte,使用指定的charsetName字符集名称所对应的字符集进行
解码生成一个char数组,然后将该char数组的引用赋给新生成String对象的内部的char数组value。

  • (2)

    public String(byte bytes[], String charsetName)
    throws UnsupportedEncodingException {
    this(bytes, 0, bytes.length, charsetName);
    }
    该构造方法调用了4.2 (1)的构造方法,将byte数组bytes,使用指定的charsetName字符集名称所对应的字符集进行解码,从而生成一个新
    的String对象。

4.3 不指定字符集

  • (1)
public String(byte bytes[], int offset, int length) {
    checkBounds(bytes, offset, length);
    this.value = StringCoding.decode(bytes, offset, length);
}

该构造方法将byte数组从bytes从offset索引位置(包括offset位置)开始,的length个byte,使用平台的默认字符集进行解码生成一个char
数组,然后将该char数组的引用赋给新生成String对象的内部的char数组value。

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

该构造方法调用4.3 (1)中的构造方法将byte数组bytes,使用平台的默认字符集进行解码,从而生成一个新的String对象。
总结:对于“以byte数组为参数的构造方法”,因为不同平台的默认字符集可能不相同,为了避免不同平台中乱码问题,建议不要使用
“不指定字符集”的构造方法。

5 以StringBuffer为参数的构造方法

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

该构造方法将StringBuffer的buffer的内部char数组value复制一份,构造一个String对象。注意该构造方法使用了synchronized语句块
保证buffer对象的线程安全性。

6 以StringBuilder为参数的构造方法

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

该构造方法将StringBuilder的builder的内部char数组value复制一份,构造一个String对象。

7 以代码点int数组为参数的构造方法(待添加)

String类常用方法

1 length()

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

该方法是求取字符串的长度。源码比较简单,返回的是String对象内部char数组value的长度。

2 isEmpty()

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

该方法是判断字符串是否为空串。该方法的源码也比较简单,判断String对象内部char数组value的长度是否为0。

3 toString()

public String toString() {
    return this;
}

该方法是将对象转换成String类型的对象。因为String对象本身就是String类型的,返回的是其本身。

4 substring(int beginIndex, int endIndex)和substring(int beginIndex)

  • substring(int beginIndex, int endIndex)
public String substring(int beginIndex, int endIndex) {
    if (beginIndex < 0) {
        throw new StringIndexOutOfBoundsException(beginIndex);
    }
    if (endIndex > value.length) {
        throw new StringIndexOutOfBoundsException(endIndex);
    }
    int subLen = endIndex - beginIndex;
    if (subLen < 0) {
        throw new StringIndexOutOfBoundsException(subLen);
    }
    return ((beginIndex == 0) && (endIndex == value.length)) ? this
            : new String(value, beginIndex, subLen);
}

该方法是求当前String对象的子串。子串为截取从原始串的beginIndex索引位置开始(包含该beginIndex)到 endIndex为止(不包含
endIndex位置)的字符串。

5 trim()

public String trim() {
    int len = value.length;
    int st = 0;
    char[] val = value;    /* avoid getfield opcode */

    while ((st < len) && (val[st] <= ' ')) {
        st++;
    }
    while ((st < len) && (val[len - 1] <= ' ')) {
        len--;
    }
    return ((st > 0) || (len < value.length)) ? substring(st, len) : this;
}

该方法返回一个去除原字符串两边小于‘ ’(空格)的子串(原字符串不变)。
【阅读String源码的过程中,发现需要Object和Character源码的基础,于是打算先阅读Object类的源码,未完待续...】

相关文章

网友评论

      本文标题:String 源码

      本文链接:https://www.haomeiwen.com/subject/xkaogqtx.html