String类:

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

是一个用 final 声明的常量类,不能被任何类所继承,而且一旦一个String对象被创建, 包含在这个对象中的字符序列是不可改变的, 包括该类后续的所有方法都是不能修改该对象的,直至该对象被销毁,这是我们需要特别注意的(该类的一些方法看似改变了字符串,其实内部都是创建一个新的字符串。接着实现了 Serializable接口,这是一个序列化标志接口,还实现了 Comparable 接口,用于比较两个字符串的大小(按顺序比较单个字符的ASCII码),后面会有具体方法实现;最后实现了 CharSequence 接口,表示是一个有序字符的集合,相应的方法后面也会介绍

字段属性:

  1. /**用来存储字符串 */
  2. private final char value[];
  3. /** 缓存字符串的哈希码 */
  4. private int hash; // Default to 0
  5. /** 实现序列化的标识 */
  6. private static final long serialVersionUID = -6849794470754667710L;

一个 String 字符串实际上是一个 char 数组。

String 类 重写了equals方法:

  1. public boolean equals(Object anObject) {
  2. if (this == anObject) {
  3. return true;
  4. }
  5. if (anObject instanceof String) {
  6. String anotherString = (String)anObject;
  7. int n = value.length;
  8. if (n == anotherString.value.length) {
  9. char v1[] = value;
  10. char v2[] = anotherString.value;
  11. int i = 0;
  12. while (n-- != 0) {
  13. if (v1[i] != v2[i])
  14. return false;
  15. i++;
  16. }
  17. return true;
  18. }
  19. }
  20. return false;
  21. }

String 类重写了 equals 方法,比较的是组成字符串的每一个字符是否相同,如果都相同则返回true,否则返回false。

String 类 重写了hashCode方法:

  1. public int hashCode() {
  2. int h = hash;
  3. if (h == 0 && value.length > 0) {
  4. char val[] = value;
  5. for (int i = 0; i < value.length; i++) {
  6. h = 31 * h + val[i];
  7. }
  8. hash = h;
  9. }
  10. return h;
  11. }

String 类的 hashCode 算法很简单,主要就是中间的 for 循环,计算公式如下:
s[0]31^(n-1) + s[1]31^(n-2) + … + s[n-1]
s 数组即源码中的 val 数组,也就是构成字符串的字符数组。这里有个数字 31 ,为什么选择31作为乘积因子,而且没有用一个常量来声明?主要原因有两个:
  ①、31是一个不大不小的质数,是作为 hashCode 乘子的优选质数之一。
  ②、31可以被 JVM 优化,31 * i = (i << 5) - i。因为移位运算比乘法运行更快更省性能。

charAt(int index)方法:

  1. public char charAt(int index) {
  2. //如果传入的索引大于字符串的长度或者小于0,直接抛出索引越界异常
  3. if ((index < 0) || (index >= value.length)) {
  4. throw new StringIndexOutOfBoundsException(index);
  5. }
  6. return value[index];//返回指定索引的单个字符
  7. }

一个字符串是由一个字符数组组成,这个方法是通过传入的索引(数组下标),返回指定索引的单个字符。

compareTo(String anotherString) 和 compareToIgnoreCase(String str) 方法:

  1. public int compareTo(String anotherString) {
  2. int len1 = value.length;
  3. int len2 = anotherString.value.length;
  4. int lim = Math.min(len1, len2);
  5. char v1[] = value;
  6. char v2[] = anotherString.value;
  7. int k = 0;
  8. while (k < lim) {
  9. char c1 = v1[k];
  10. char c2 = v2[k];
  11. if (c1 != c2) {
  12. return c1 - c2;
  13. }
  14. k++;
  15. }
  16. return len1 - len2;
  17. }

  源码也很好理解,该方法是按字母顺序比较两个字符串,是基于字符串中每个字符的 Unicode 值。当两个字符串某个位置的字符不同时,返回的是这一位置的字符 Unicode 值之差,当两个字符串都相同时,返回两个字符串长度之差。
  compareToIgnoreCase() 方法在 compareTo 方法的基础上忽略大小写,我们知道大写字母是比小写字母的Unicode值小32的,底层实现是先都转换成大写比较,然后都转换成小写进行比较。

concat(String str) 方法:

  1. //该方法是将指定的字符串连接到此字符串的末尾。
  2. public String concat(String str) {
  3. int otherLen = str.length();
  4. if (otherLen == 0) {
  5. return this;
  6. }
  7. int len = value.length;
  8. char buf[] = Arrays.copyOf(value, len + otherLen);
  9. str.getChars(buf, len);
  10. return new String(buf, true);
  11. }

首先判断要拼接的字符串长度是否为0,如果为0,则直接返回原字符串。如果不为0,则通过 Arrays 工具类(后面会详细介绍这个工具类)的copyOf方法创建一个新的字符数组,长度为原字符串和要拼接的字符串之和,前面填充原字符串,后面为空。接着在通过 getChars 方法将要拼接的字符串放入新字符串后面为空的位置。
  注意:返回值是 new String(buf, true),也就是重新通过 new 关键字创建了一个新的字符串,原字符串是不变的。这也是前面我们说的一旦一个String对象被创建, 包含在这个对象中的字符序列是不可改变的
**

indexOf(int ch) 和 indexOf(int ch, int fromIndex) 方法:

  1. public int indexOf(int ch, int fromIndex) {
  2. final int max = value.length;//max等于字符的长度
  3. if (fromIndex < 0) {//指定索引的位置如果小于0,默认从 0 开始搜索
  4. fromIndex = 0;
  5. } else if (fromIndex >= max) {
  6. //如果指定索引值大于等于字符的长度(因为是数组,下标最多只能是max-1),直接返回-1
  7. return -1;
  8. }
  9. if (ch < Character.MIN_SUPPLEMENTARY_CODE_POINT) {//一个char占用两个字节,如果ch小于2的16次方(65536),绝大多数字符都在此范围内
  10. final char[] value = this.value;
  11. for (int i = fromIndex; i < max; i++) {//for循环依次判断字符串每个字符是否和指定字符相等
  12. if (value[i] == ch) {
  13. return i;//存在相等的字符,返回第一次出现该字符的索引位置,并终止循环
  14. }
  15. }
  16. return -1;//不存在相等的字符,则返回 -1
  17. } else {//当字符大于 65536时,处理的少数情况,该方法会首先判断是否是有效字符,然后依次进行比较
  18. return indexOfSupplementary(ch, fromIndex);
  19. }
  20. }