String源码分析

  String类内部维护了一个char[]类型的value用来存储字符串,相对来说源码较为简单些。

1.不可变性

String的不可变主要体现在三个方面:

  • String类被定义为final类型,不可被继承
  • String中的value[]被定义为final
  • String中的所有生成新的String的操作底层都调用Array.copy或者System.copy来生成一个新的String对象

2.构造函数

  String的有多个构造函数,但以下几个较为特殊

1
2
3
4
5
6
7
8
9
public String(String original) {
this.value = original.value;
this.hash = original.hash;
}
String(char[] value, boolean share) {
// assert share : "unshared not supported";
this.value = value;
}

  以上两个是较为特殊的一类构造函数,第一个用一个现成的String来初始化一个新的String对象,构造方法直接将新的String对象的value指向老的value对象。由于String是不可变的,所以这里不需要重新copy一份value的对象。第二个构造方法看似会破坏String类型的不可变性(当参数value变化时String也会变化),但该构造方法并没有声明为public,只允许在包内使用,被声明为public的String(char value[])底层调用的是Array.copy来实现底层数据拷贝的,上述的两个构造函数已经不建议使用

1
2
3
4
5
6
7
8
9
10
11
12
13
public String(char value[], int offset, int count) {
if (offset < 0) {
throw new StringIndexOutOfBoundsException(offset);
}
if (count < 0) {
throw new StringIndexOutOfBoundsException(count);
}
// 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数组(byte[]),offset偏移位置及count偏移量。底层调用Arrays.copy函数来进行深度复制。

1
2
3
4
5
6
7
8
9
public String(StringBuffer buffer) {
synchronized(buffer) { //保证线程安全
this.value = Arrays.copyOf(buffer.getValue(), buffer.length());
}
}
public String(StringBuilder builder) {
this.value = Arrays.copyOf(builder.getValue(), builder.length());
}

  上述两个构造函数入参分别是StringBuffer和StringBuilder,底层都是调用Arrays.copyOf,唯一不同的是StringBuffer是线程不安全的,所有调用时需要用synchronized关键字。

3.其他方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
static int indexOf(char[] source, int sourceOffset, int sourceCount,
char[] target, int targetOffset, int targetCount,
int fromIndex) {
if (fromIndex >= sourceCount) {
return (targetCount == 0 ? sourceCount : -1);
}
if (fromIndex < 0) {
fromIndex = 0;
}
if (targetCount == 0) {
return fromIndex;
}
char first = target[targetOffset];
int max = sourceOffset + (sourceCount - targetCount);
for (int i = sourceOffset + fromIndex; i <= max; i++) {
/* Look for first character. */
if (source[i] != first) {
while (++i <= max && source[i] != first);
}
/* Found first character, now look at the rest of v2 */
if (i <= max) {
int j = i + 1;
int end = j + targetCount - 1;
for (int k = targetOffset + 1; j < end && source[j]
== target[k]; j++, k++);
if (j == end) {
/* Found whole string. */
return i - sourceOffset;
}
}
}
return -1;
}
static int lastIndexOf(char[] source, int sourceOffset, int sourceCount,
char[] target, int targetOffset, int targetCount,
int fromIndex) {
/*
* Check arguments; return immediately where possible. For
* consistency, don't check for null str.
*/
int rightIndex = sourceCount - targetCount;
if (fromIndex < 0) {
return -1;
}
if (fromIndex > rightIndex) {
fromIndex = rightIndex;
}
/* Empty string always matches. */
if (targetCount == 0) {
return fromIndex;
}
int strLastIndex = targetOffset + targetCount - 1;
char strLastChar = target[strLastIndex];
int min = sourceOffset + targetCount - 1;
int i = min + fromIndex;
startSearchForLastChar:
while (true) {
while (i >= min && source[i] != strLastChar) {
i--;
}
if (i < min) {
return -1;
}
int j = i - 1;
int start = j - (targetCount - 1);
int k = strLastIndex - 1;
while (j > start) {
if (source[j--] != target[k--]) {
i--;
continue startSearchForLastChar;
}
}
return start - sourceOffset + 1;
}
}

  indexOf和lastIndexOf主要是index和lastIndex函数的底层调用,通读代码会发现底层实现并没有特别牛逼的kmp算法,仍然是一个字符一个字符扫描实现的。其中lastIndexOf还是用了continue startSearchForLastChar;相对来说比较少见。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
public String replace(char oldChar, char newChar) {
if (oldChar != newChar) {
int len = value.length;
int i = -1;
char[] val = value; /* avoid getfield opcode */
while (++i < len) {
if (val[i] == oldChar) {
break;
}
}
//如果找不到则返回this
if (i < len) {
char buf[] = new char[len];
for (int j = 0; j < i; j++) {
buf[j] = val[j];
}
while (i < len) {
char c = val[i]; //替换
buf[i] = (c == oldChar) ? newChar : c;
i++;
}
//返回新的String,利用上述包内的非public构造函数
return new String(buf, true);
}
}
return this;
}

  replace用于将String对象中的一个字符替换为另一个字符,如果找不到制定的字符则返回本身,如果找到则会另建一个新的String对象返回。