String源码解析(JDK1.8)

1、string类的定义

public final class String 
    implements java.io.Serializable, Comparable<String>, CharSequence {}
  • java.io.Serializable
    序列化接口没有任何方法和域,仅用于标识序列化的语意

  • Comparable
    接口只有一个compareTo(T 0)接口,用于对两个实例化对象比较大小

  • CharSequence
    接口是一个只读的字符序列。包括length(), charAt(int index), subSequence(int start, int end)这几个API接口,值得一提的是,StringBuffer和StringBuild也是实现了改接口

2、主要变量

/**用来存储字符串  */
private final char value[];

/** 缓存字符串的哈希码 */
private int hash; // Default to 0

/** 实现序列化的标识 */
private static final long serialVersionUID = -6849794470754667710L;

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

3、构造方法
 String 类的构造方法很多。可以通过初始化一个字符串,或者字符数组,或者字节数组等等来创建一个 String 对象。
 在这里插入图片描述

String str1 = "abc";//注意这种字面量声明的区别,文末会详细介绍
String str2 = new String("abc");
String str3 = new String(new char[]{'a','b','c'});

4、常用方法

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

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

  • 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 算法很简单,主要就是中间的 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)
public char charAt(int index) {
   //如果传入的索引大于字符串的长度或者小于0,直接抛出索引越界异常
   if ((index < 0) || (index >= value.length)) {
       throw new StringIndexOutOfBoundsException(index);
   }
   //返回指定索引的单个字符
   return value[index];
}
  • compareTo(String anotherString) 和 compareToIgnoreCase(String str)

我们先看看 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 值之差,当两个字符串都相同时,返回两个字符串长度之差。

compareToIgnoreCase() 方法在 compareTo 方法的基础上忽略大小写,我们知道大写字母是比小写字母的Unicode值小32的,底层实现是先都转换成大写比较,然后都转换成小写进行比较。

  • concat(String 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);
    }

首先判断要拼接的字符串长度是否为0,如果为0,则直接返回原字符串。如果不为0,则通过 Arrays 工具类(后面会详细介绍这个工具类)的copyOf方法创建一个新的字符数组,长度为原字符串和要拼接的字符串之和,前面填充原字符串,后面为空。接着在通过 getChars 方法将要拼接的字符串放入新字符串后面为空的位置。

注意:返回值是 new String(buf, true),也就是重新通过 new 关键字创建了一个新的字符串,原字符串是不变的。这也是前面我们说的一旦一个String对象被创建, 包含在这个对象中的字符序列是不可改变的。

  • indexOf(int ch) 和 indexOf(int ch, int fromIndex)

indexOf(int ch),参数 ch 其实是字符的 Unicode 值,这里也可以放单个字符(默认转成int),作用是返回指定字符第一次出现的此字符串中的索引。其内部是调用 indexOf(int ch, int fromIndex),只不过这里的 fromIndex =0 ,因为是从 0 开始搜索;而 indexOf(int ch, int fromIndex) 作用也是返回首次出现的此字符串内的索引,但是从指定索引处开始搜索。


转自:https://www.cnblogs.com/ysocean/p/8571426.html#_label0

猜你喜欢

转载自blog.csdn.net/qidasheng2012/article/details/84634014