JDK源码解析系列之String 之一

1 接口解释
(1)Serializable 这个序列化接口没有任何方法和域,仅用于标识序列化的语意。
(2)用于对两个实例化对象比较大小

Comparable<String>

(3)CharSequence 这个接口是一个只读的字符序列。包括length(),
charAt(int index), subSequence(int start, int end)这几个API接口

2 主要变量

private final char value[];

可以看到,value[]是存储String的内容的,即当使用String str = “abcd”;
的时候,本质上,"abcd"是存储在一个char类型的数组中的。

private int hash;

而hash是String实例化的hashcode的一个缓存。因为String经常被用于比较,比如在HashMap中。
如果每次进行比较都重新计算hashcode的值的话,那无疑是比较麻烦的,而保存一个hashcode的缓存无疑能优化这样的操作。

private static final long serialVersionUID = -6849794470754667710L;

Java的序列化机制是通过在运行时判断类的serialVersionUID来验证版本一致性的。在进行反序列化时,JVM会把传来
的字节流中的serialVersionUID与本地相应实体(类)的serialVersionUID进行比较,如果相同就认为是一致的,可以进行反序
列化,否则就会出现序列化版本不一致的异常,如果我们不希望通过编译来强制划分软件版本,即实现序列化接口的实体能够兼容先前版本,
未作更改的类,就需要显式地定义一个名为serialVersionUID,类型为long的变量,不修改这个变量值的序列化实体都可以相互进行
串行化和反串行化

private static final ObjectStreamField[] serialPersistentFields =
    new ObjectStreamField[0];

3 构造方法
String支持多种初始化方法,包括接收String,char[],byte[],StringBuffer等多种参数类型的初始化方法。
但本质上,其实就是将接收到的参数传递给全局变量value[]。

public String()
public String(String original)
public String(char value[])
public String(char value[], int offset, int count)
public String(int[] codePoints, int offset, int count)
public String(byte ascii[], int hibyte, int offset, int count)
public String(byte ascii[], int hibyte)
public String(byte bytes[], int offset, int length, String charsetName)
public String(byte bytes[], int offset, int length, Charset charset)
public String(byte bytes[], String charsetName)
public String(byte bytes[], Charset charset)
public String(byte bytes[], int offset, int length)
public String(byte bytes[])
public String(StringBuffer buffer)
public String(StringBuilder builder)

4 内部方法
(1)知道了String其实内部是通过char[]实现的,那么就不难发现length(),isEmpty(),charAt()这些方法其实就是在内部调用数组的方法。

public int length() {
   	return value.length;
}
public boolean isEmpty() {
	return value.length == 0;
}
public char charAt(int index) {
	if ((index < 0) || (index >= value.length)) {
		throw new StringIndexOutOfBoundsException(index);
	}
	return value[index];
}

(2)返回指定索引的代码点

public int codePointAt(int index) {
	if ((index < 0) || (index >= value.length)) {
		throw new StringIndexOutOfBoundsException(index);
	}
	return Character.codePointAtImpl(value, index, value.length);
}

(3)返回指定索引前一个代码点

public int codePointBefore(int index) {
	int i = index - 1;
	if ((i < 0) || (i >= value.length)) {
		throw new StringIndexOutOfBoundsException(index);
	}
	return Character.codePointBeforeImpl(value, index, 0);
}

(4)返回指定起始到结束段内字符个数

public int codePointCount(int beginIndex, int endIndex) {
    if (beginIndex < 0 || endIndex > value.length || beginIndex > endIndex) {
        throw new IndexOutOfBoundsException();
    }
    return Character.codePointCountImpl(value, beginIndex, endIndex - beginIndex);
 }

(5)返回指定索引加上codepointOffset后得到的索引值

public int offsetByCodePoints(int index, int codePointOffset) {
    if (index < 0 || index > value.length) {
        throw new IndexOutOfBoundsException();
    }
    return Character.offsetByCodePointsImpl(value, 0, value.length,
            index, codePointOffset);
}

(6)将字符串复制到dst数组中,复制到dst数组中的起始位置可以指定。值得注意的是,该方法并没有检测复制到dst数组后是否越界。

void getChars(char dst[], int dstBegin) {
    System.arraycopy(value, 0, dst, dstBegin, value.length);
}
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);
}

(7)获取当前字符串的二进制

public void getBytes(int srcBegin, int srcEnd, byte 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);
   }
   Objects.requireNonNull(dst);

   int j = dstBegin;
   int n = srcEnd;
   int i = srcBegin;
   char[] val = value;   /* avoid getfield opcode */

   while (i < n) {
       dst[j++] = (byte)val[i++];
   }
}

(8)public byte[] getBytes(String charsetName)

public byte[] getBytes(String charsetName) throws UnsupportedEncodingException {
    if (charsetName == null) throw new NullPointerException();
    return StringCoding.encode(charsetName, value, 0, value.length);
}

(9)public byte[] getBytes()

public byte[] getBytes() {
  return StringCoding.encode(value, 0, value.length);
}

(10) public boolean equals(Object anObject)

 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;
}

(11)public int 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;
}

(11)public boolean contentEquals(CharSequence cs)
这个主要是用来比较String和StringBuffer或者StringBuild的内容是否一样。可以看到传入参数是CharSequence ,
这也说明了StringBuffer和StringBuild同样是实现了CharSequence。源码中先判断参数是从哪一个类实例化来的,
再根据不同的情况采用不同的方案,不过其实大体都是采用上面那个for循环的方式来进行判断两字符串是否内容相同。

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;
   }

(12)public int compareTo(String anotherString)
这个就是String对Comparable接口中方法的实现了。其核心就是那个while循环,通过从第一个开始比较每一个字符,
当遇到第一个较小的字符时,判定该字符串小。

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;
   }

(13)这个也是比较字符串大小,规则和上面那个比较方法基本相同,差别在于这个方法忽略大小写

public int compareToIgnoreCase(String str) {
    return CASE_INSENSITIVE_ORDER.compare(this, str);
}

(14)比较该字符串和其他一个字符串从分别指定地点开始的n个字符是否相等。看代码可知道,其原理还是通过一个while去循环对应的比较区域进行判断,但在比较之前会做判定,判定给定参数是否越界。

public boolean regionMatches(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) {
        if (ta[to++] != pa[po++]) {
            return false;
        }
    }
    return true;
}

(15)判断当前字符串是否以某一段其他字符串开始的,和其他字符串比较方法一样,其实就是通过一个while来循环比较。

 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;
}  

(16)public int indexOf(int ch, int fromIndex)

public int indexOf(int ch, int fromIndex) {
	final int max = value.length;
	if (fromIndex < 0) {
		fromIndex = 0;
	} else if (fromIndex >= max) {
	// Note: fromIndex might be near -1>>>1.
		return -1;
	}
	if (ch < Character.MIN_SUPPLEMENTARY_CODE_POINT) {
	// handle most cases here (ch is a BMP code point or a
	// negative value (invalid code point))
		final char[] value = this.value;
		for (int i = fromIndex; i < max; i++) {
			if (value[i] == ch) {
				return i;
			}
		}
		return -1;
	} else {
		 return indexOfSupplementary(ch, fromIndex);
	}
}

(17)public int indexOf(int ch)

public int indexOf(int ch) {
	return indexOf(ch, 0);
}

(18) 这个方法可以返回字符串中一个子串,看最后一行可以发现,其实就是指定头尾,然后构造一个新的字符串。

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

(19)concat的作用是将str拼接到当前字符串后面,通过代码也可以看出其实就是建一个新的字符串。

public String concat(String str) {
	int otherLen = str.length();
	if (otherLen == 0) {
		return this;
	}
	int len = value.length;
	char buf[] = Arrays.copyOf(value, len + otherLen);
	str.getChars(buf, len);
	return new String(buf, true);
}

(20)替换操作,主要是将原来字符串中的oldChar全部替换成newChar。看这里实现,主要是先找到第一个所要替换的字符串的位置 i ,将i之前的字符直接复制到一个新char数组。然后从 i 开始再对每一个字符进行判断是不是所要替换的字符。

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;
}

(21)这个函数平时用的应该比较多,删除字符串前后的空格,原理是通过找出前后第一个不是空格的字符串,返回原字符串的该子串。

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;
}

(22) 返回一个新的字符类型的字符串

public CharSequence subSequence(int beginIndex, int endIndex) {
	return this.substring(beginIndex, endIndex);
}
   
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值