1.概览
String类是Java开发中最最常用的类,所以源码阅读首先选择String类。
2.实现的接口
String类一共实现了三个接口,具体如下:
- Serializable:标记接口,表示该类可以序列化
- Comparable: 具有比较能力
- CharSequence:实现了字节序列操作
3.属性
String类一共有三个属性,具体如下。
private final char value[];
private int hash;
private static final long serialVersionUID;
3.1 value
value是不可变的字符数组。String类的所有操作都是基于对value的操作。从value不可变的性质,可以推出String是不可变的类型。
3.2 hash
String的哈希值。
3.3 serialVersionUID
序列化版本标识。
4.构造方法
String支持默认构造方法,还支持从String构造、byte[]构造、char[]构造、codePoints构造、StringBuffer构造和StringBuffer构造。
4.1 默认构造
String的默认构造得到的是空字符串“”
4.2 从String构造
从String构造,得到的String。两个String共享同一个内存地址。因为String是不修改的,所以可共享同一个内存地址。
4.3 从byte数组构造
使用字节数组构造String,需要指定编码方式,否则,默认使用系统默认编码方式进行构造String。
4.4 从char数组构造
使用char数组构造String时,使用的是深拷贝。深思一下,使用深拷贝可以避免传参进来的字符数组改动导致String内容改变。
4.5 从codePoints构造(使用int数组构造)
使用码点构造String。对于编码方式我不太熟悉,就不展开讲了。
4.6 从StringBuffer构造
StringBuffer是线程安全的,在构造String时,注意用synchronized关键字锁一下。另外,使用StringBuffer构造String,用的是深拷贝。
4.7 从StringBuilder构造
使用的是深拷贝。
5. 长度相关方法
5.1 length方法
返回String的长度
5.2 isEmpty方法
返回String是否为空
6. 索引相关方法
6.1 charAt方法
获取指定index位置的char
6.2 codePointAt方法
获取指定index位置的codePoint
6.3 codePointBefore方法
获取指定index-1位置的codepoint
6.4 codePointCount方法
获取真实字符个数
6.5 offsetByCodePoints方法
从index处,按照代码点进行移动,并返回索引
6.6 getChars方法
从指定位置复制到char数组
6.7 getBytes方法
返回bytes数组
7. 比较方法
7.1 equals方法
public boolean equals(Object anObject) {
if (this == anObject) {
return true;
}
if (anObject instanceof String) {
String anotherString = (String)anObject;
int n = value.length;
if (n == anotherString.value.length) {
char v1[] = value;
char v2[] = anotherString.value;
int i = 0;
while (n-- != 0) {
if (v1[i] != v2[i])
return false;
i++;
}
return true;
}
}
return false;
}
- 先比较地址是否相同,若相同,返回true
- 在长度相同的情况下比较char是否相同,若不同,返回false;若全部相同,返回true
- 其他情况返回false
7.2 contentEquals方法
比较String和其他CharSequence内容是否相等的方法
public boolean contentEquals(CharSequence cs) {
// Argument is a StringBuffer, StringBuilder
if (cs instanceof AbstractStringBuilder) {
if (cs instanceof StringBuffer) {
synchronized(cs) {
return nonSyncContentEquals((AbstractStringBuilder)cs);
}
} else {
return nonSyncContentEquals((AbstractStringBuilder)cs);
}
}
// Argument is a String
if (cs instanceof String) {
return equals(cs);
}
// Argument is a generic CharSequence
char v1[] = value;
int n = v1.length;
if (n != cs.length()) {
return false;
}
for (int i = 0; i < n; i++) {
if (v1[i] != cs.charAt(i)) {
return false;
}
}
return true;
}
- 查看cs是否是StringBuilder的实例,若是,使用synchronized关键字锁住,进行内容比较
- 若cs是StringBuilder的实例,进行内容比较
- cs是String类型,直接调用equals方法
- cs是其他charSequence类型,则按一般的方法进行比较
7.3 equalsIgnoreCase方法
忽略大小写,比较字符
public boolean equalsIgnoreCase(String anotherString) {
return (this == anotherString) ? true
: (anotherString != null)
&& (anotherString.value.length == value.length)
&& regionMatches(true, 0, anotherString, 0, value.length);
}
- 比较地址是否相同,相同,返回true
- 其他情况,需要满足不为null 长度相同 内容忽略大小写一致,则返回true
7.4 regionMatches方法
比较String是否相等,可以忽略大小写比较
public boolean regionMatches(boolean ignoreCase, int toffset,
String other, int ooffset, int len) {
char ta[] = value;
int to = toffset;
char pa[] = other.value;
int po = ooffset;
// Note: toffset, ooffset, or len might be near -1>>>1.
if ((ooffset < 0) || (toffset < 0)
|| (toffset > (long)value.length - len)
|| (ooffset > (long)other.value.length - len)) {
return false;
}
while (len-- > 0) {
char c1 = ta[to++];
char c2 = pa[po++];
if (c1 == c2) {
continue;
}
if (ignoreCase) {
// If characters don't match but case may be ignored,
// try converting both characters to uppercase.
// If the results match, then the comparison scan should
// continue.
char u1 = Character.toUpperCase(c1);
char u2 = Character.toUpperCase(c2);
if (u1 == u2) {
continue;
}
// Unfortunately, conversion to uppercase does not work properly
// for the Georgian alphabet, which has strange rules about case
// conversion. So we need to make one last check before
// exiting.
if (Character.toLowerCase(u1) == Character.toLowerCase(u2)) {
continue;
}
}
return false;
}
return true;
}
- 边界校验,不通过直接false
- 大小写敏感的情况,直接比较字符
- 大小写不敏感的情况,统一转换成大写处理,由于德语特例,所以,大写校验不过,小写再校验一次
7.5 compareTo方法
字符串大小比较
public int compareTo(String anotherString) {
int len1 = value.length;
int len2 = anotherString.value.length;
int lim = Math.min(len1, len2);
char v1[] = value;
char v2[] = anotherString.value;
int k = 0;
while (k < lim) {
char c1 = v1[k];
char c2 = v2[k];
if (c1 != c2) {
return c1 - c2;
}
k++;
}
return len1 - len2;
}
- 比较相同index的字符,若字符不一致,返回相减结果
- 若字符都一致,返回长度相减结果
7.6 startsWitch方法
查看从offset处,是否是以prefix开头的字符串
public boolean startsWith(String prefix, int toffset) {
char ta[] = value;
int to = toffset;
char pa[] = prefix.value;
int po = 0;
int pc = prefix.value.length;
// Note: toffset might be near -1>>>1.
if ((toffset < 0) || (toffset > value.length - pc)) {
return false;
}
while (--pc >= 0) {
if (ta[to++] != pa[po++]) {
return false;
}
}
return true;
}
8. 不分类了
8.1 hashCode方法
计算String的hashCode
8.2 subString
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);
}
利用了构造函数
8.3 concat方法
public String concat(String str) {
if (str.isEmpty()) {
return this;
}
int len = value.length;
int otherLen = str.length();
char buf[] = Arrays.copyOf(value, len + otherLen);
str.getChars(buf, len);
return new String(buf, true);
}
搞一个扩容后的数组,利用getChars填充,再使用构造函数返回
8.4 replace方法
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;
}
}
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++;
}
return new String(buf, true);
}
}
return this;
}
该方法使用newChar替代所有的oldChar并返回一个新的String。 替换步骤:
- 判断oldChar和newChar是否是同一个char,是的话,直接返回。不是的话,继续一下步骤。
- 判断oldChar出现在String中,若没出现,直接返回该String,若出现,继续下一步骤
- 进行字符串替换
8.5 contains方法
public boolean contains(CharSequence s) {
return indexOf(s.toString()) > -1;
}
contains方法调用内部的indexof方法,所以重点分析一下indexof方法。
static int indexOf(char[] source, int sourceOffset, int sourceCount,
char[] target, int targetOffset, int targetCount,
int fromIndex) {
// 搜索的位置超过了source子串长度,具体返回啥,看target子串长度
// target子串长度0,返回source子串长度
// target子串长度不是0,返回-1,即不合法
if (fromIndex >= sourceCount) {
return (targetCount == 0 ? sourceCount : -1);
}
// source子串搜索的起始位置若小于0,置为0
if (fromIndex < 0) {
fromIndex = 0;
}
// 若target子串长度0,则返回搜索的起始位置
if (targetCount == 0) {
return fromIndex;
}
// 搜索的第一个字符
char first = target[targetOffset];
// 搜索的合法位置
// 比如一个串长度9,一个串长度6,搜索的合法位置为[0, 5] [1, 6] [2, 7] [3, 8]
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;
}
这个方法我看了很久,不知道这些参数代表的具体含义。终于,在搜索引擎的帮助下,明白了这个方法的作用,这几个参数代表的东西。
子串一:target字符串中,由targetOffset和targetCount两者确定的target子串
子串二:source字符串中由sourceOffset和sourceCount以及fromIndex确定的子串
判断子串二是否包含子串一,若包含,返回子串二包含的index
其中,
- source代表比较长的字符串,是在这个串上做搜索操作的
- sourceOffset,source子串的起始位置
- sourceCount,source子串的长度
- target代表比较短的字符串,通常是拿来搜索的串
- targetOffset,target子串的起始位置
- targetCount,target子串的长度
- fromIndex, 表示source子串搜索的起始位置 所以,该函数的具体作用,如上注释所示。