前言
String类属于Java.lang包下的一个类,在日常开发中使用最频繁的一个类,本文主要是本人在阅读String源码过程的相关笔记。
成员变量
private final char value[];
private int hash;
复制代码
String是一个final声明的类,字符串是通过char[]来存储,并且使用hash变量存储String的hashCode。
构造函数
String提供了比较多的构造函数,如下:

主要有以下几类:
- 根据char[]来构造。
- 根据int[]来构造
- 根据byte[]来构造。
- 根据StringBuffer来构造。
- 根据StringBuilder来构造。
其中根据int[]来构造的主要适用于码位,根据byte[]构造的主要分为unicode、ascii、byte这三种情况,鉴于本人对码位以及unicode, ascii相关知识没有深入学习,因此这几个构造函数跟相关的函数就暂时不作分析,日后希望可以补上这个短板。
根据char[]构造String
public String(char value[]) {
this.value = Arrays.copyOf(value, value.length);
}
复制代码
由于char[]是存放到堆里面的,若使用this.value=value的方式赋值,后续对this.value的相关操作会影响到value[],因此采用了Arrays.copyOf来完成深拷贝的工作。
根据byte[]来构造
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);
}
private static void checkBounds(byte[] bytes, int offset, int length) {
if (length < 0)
throw new StringIndexOutOfBoundsException(length);
if (offset < 0)
throw new StringIndexOutOfBoundsException(offset);
if (offset > bytes.length - length)
throw new StringIndexOutOfBoundsException(offset + length);
}
复制代码
在构造前,源码中利用了checkBounds来校offset+length是否越界了,也就是offset + length > bytes.length若为真,那么就抛出异常。
校验都通过后,利用了String.decode来讲bytes转换为String。
根据StringBuffer来构造
public String(StringBuffer buffer) {
synchronized(buffer) {
this.value = Arrays.copyOf(buffer.getValue(), buffer.length());
}
}
复制代码
由于StringBuffer是线程安全的,为了避免在buffer.getValue()的时候,buffer还在操作字符串,就需要添加synchronized来对buffer进行同步处理。StringBuilder的构造与StringBuffer差不多,只是缺少了syncronized。
charAt源码分析
public char charAt(int index) {
if ((index < 0) || (index >= value.length)) {
throw new StringIndexOutOfBoundsException(index);
}
return value[index];
}
复制代码
通过源码,可以看出charAt实际是这些操作value数组的,通过入参index就可以获取对应的值。
getChars源码分析
public void getChars(int srcBegin, int srcEnd, char dst[], int dstBegin) {
if (srcBegin < 0) {
throw new StringIndexOutOfBoundsException(srcBegin);
}
if (srcEnd > value.length) {
throw new StringIndexOutOfBoundsException(srcEnd);
}
if (srcBegin > srcEnd) {
throw new StringIndexOutOfBoundsException(srcEnd - srcBegin);
}
System.arraycopy(value, srcBegin, dst, dstBegin, srcEnd - srcBegin);
}
复制代码
以上的函数主要对入参做相关的合法性校验,校验通过后,通过System.arraycopy来对value[]进行复制。
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;
}
复制代码
大部分类的equlas的通常都先通过this == anObject方法来判断是否相等,如果条件达到,直接返回true。如果不一致,那么就判断内容是否相等。
String的equals判断内容是否相等前,会判断字符串的长度是否相等,如果不相等,那么就通过循环逐位判断,若存在不相等的字符,就返回false。
nonSyncContentEquals源码分析
private boolean nonSyncContentEquals(AbstractStringBuilder sb) {
char v1[] = value;
char v2[] = sb.getValue();
int n = v1.length;
if (n != sb.length()) {
return false;
}
for (int i = 0; i < n; i++) {
if (v1[i] != v2[i]) {
return false;
}
}
return true;
}
复制代码
主要用于String实例与StringBuffer以及StringBuilder实例判断是否相等,主要原理还是先比较字符长度是否一致,最后再逐位比较字符是否相等。
contentEquals源码分析
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;
}
复制代码
继承CharSequence类的子类有StringBuilder、StringBuffer以及String,因此针对StringBuilder以及StringBuffer需要进行另一种方式的比较。
通过cs instanceof AbstractStringBuilder,就可以知道cs为StringBuffer或者StringBuilder了,再通过cs instanceof StringBuffer来判断是否需要加入同步锁synchronized。
通过cs instanceof String来判断cs是否为String类,若是,那么直接调用String.equals来比较即可。
若都不属于以上这几种情况,则就需要根据CharSequence来判断字符是否相等了,采用的原理也是逐位判断。
equalsIgnoreCase源码分析
public boolean equalsIgnoreCase(String anotherString) {
return (this == anotherString) ? true
: (anotherString != null)
&& (anotherString.value.length == value.length)
&& regionMatches(true, 0, anotherString, 0, value.length);
}
复制代码
忽略字符串的大小写后,判断字符串是否相等。在调用regionMatches方法比较前,需要先符合anotherString与this、this.value的字符串长度一致。regionMatches方法在以下分析。
regionMatches方法

关于以上统一转换大写字符比较一次后,若不成功,再统一转换成小写字符进行一次,通过注释可知,主要度其他字母不能够转换大写的一个补偿处理。
hashCode源码分析
public int hashCode() {
int h = hash;
if (h == 0 && value.length > 0) {
char val[] = value;
for (int i = 0; i < value.length; i++) {
h = 31 * h + val[i];
}
hash = h;
}
return h;
}
复制代码
String的hashCode计算方式为:s[0]*31^(n-1) + s[1]*31^(n-2) + … + s[n-1],其中s[i]是字符串的第i个字符, n是字符串的长度, ^表示幂。 (空字符串的哈希值为零。)
从源码中可以看出hashCode的计算使用了31作为乘子,主要考虑有以下原因:
- 31是一个不大不小的质数,是作为hashCode乘子的优选质数之一。另外一些相近的质数,比如37、41、43等,也都是不错的选择。那么为什么选中了31呢?请看第二个原因。
- 31可以被JVM优化,31 ∗ i =(i<<5)- i。
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;
}
复制代码
该方法源码很好理解,即按字母顺序比较两个字符串,是基于字符串中每个字符的Unicode值。当两个字符串某个位置的字符不同时,返回的是这一位置的字符Unicode值之差,当两个字符串都相同时,则返回两个字符串长度之差。
参考
- java修炼指南:高频源码解析























![[桜井宁宁]COS和泉纱雾超可爱写真福利集-一一网](https://www.proyy.com/skycj/data/images/2020-12-13/4d3cf227a85d7e79f5d6b4efb6bde3e8.jpg)

![[桜井宁宁] 爆乳奶牛少女cos写真-一一网](https://www.proyy.com/skycj/data/images/2020-12-13/d40483e126fcf567894e89c65eaca655.jpg)