String源码浅谈

String这个类可以说是我们使用得最为频繁的类之一了,前几次去面试,都被问到String的底层源码,回答得都不是很好,今天就来谈谈一下String的源码。
一、String类

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

String类被是被final修饰的,表明该类不可被继承,有关于关键词final的作用,请移步我的另外一篇文章【JAVA】关键词final的作用。值得一提的是,StringBuilder,StringBuffer类都是被final修饰的。
二、String类的属性

      /** The value is used for character storage. */
        private final char value[];
     
        /** Cache the hash code for the string */
        private int hash; // Default to 0
     
        /** use serialVersionUID from JDK 1.0.2 for interoperability */
        private static final long serialVersionUID = -6849794470754667710L;

(1)char value[]

String底层的存储结构是一个字符类型的数组,同样也是被final修饰,因此一旦这个字符数组被创建后,value变量不可再指向其他数组,但是可以改变value数组中某一个元素的值。

(2)int hash

hash用来保存某一个String实例自己的哈希值,可以说是哈希值的一个缓存,因此String特别适合放入HashMap中,作为key来使用。每次插入一个键值対时,不需要重新计算key的哈希值,直接取出key的缓存hash值即可,在一定程度上,加快了HashMap的效率。

(3)long serialVersionUID

用于保证版本一致性。由于String实现了Serializable接口,因此需要拥有一个序列化的ID。序列化时,将此ID与对象一并写入到文件中,反序列化时,检测该类中的ID与文件中的ID是否一致,一致的话,说明版本一致,序列化成功。
三、String类的构造函数

(1)无参构造函数,创建一个空字符串,即"",用得地方不多。

        public String() {
            this.value = new char[0];
        }

(2)接收一个String实例的构造函数

        public String(String original) {
            this.value = original.value;
            this.hash = original.hash;
        }

(3)接收一个字符数组,利用Arrays.copyOf()方法进行拷贝

        public String(char value[]) {
            this.value = Arrays.copyOf(value, value.length);
        }

进入到Arrays.copyOf()方法中,发现调用的是System.arraycopy()方法,Arrays.copyOf()方法如下

        public static char[] copyOf(char[] original, int newLength) {
            char[] copy = new char[newLength];
            System.arraycopy(original, 0, copy, 0,
                             Math.min(original.length, newLength));
            return copy;
        }

而System.arraycopy()方法是一个本地方法,由其他语言实现。

从以上源码,可以看得出,这个构造方法没有直接使用传入的字符数组的引用,而是使用该数组的一个拷贝,保证了String类的不可变性。我们无法通过在外部改变此数组中某些元素的值,来改变构造后的String的值。

同样在toCharArray()方法中,也是返回一个基于字符数组的拷贝,并没有直接直接返回value数组。

        public char[] toCharArray() {
            // Cannot use Arrays.copyOf because of class initialization order issues
            char result[] = new char[value.length];
            System.arraycopy(value, 0, result, 0, value.length);
            return result;
        }

另外,有关于System.arraycopy()、Arrays.copyOf()之间的效率问题,可以参考我的另外一篇文章【JAVA】数组复制效率的比较

(4)接收一个字符数组,从offset位置开始复制,一共选取count位

        public String(char value[], int offset, int count) {
            if (offset < 0) {
                throw new StringIndexOutOfBoundsException(offset);
            }
            if (count < 0) {
                throw new StringIndexOutOfBoundsException(count);
            }
            // Note: offset or count might be near -1>>>1.
            if (offset > value.length - count) {
                throw new StringIndexOutOfBoundsException(offset + count);
            }
            this.value = Arrays.copyOfRange(value, offset, offset+count);
        }

其他的构造函数的原理大同小异,这里就不再说明了。
四、String类的其他方法

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

String类重写了equals()方法,判断两个String实例代表的字符串是否相同。

判断规则:

如果两个Stirng实例压根就是一个对象,即它们的内存地址相同,则直接返回true。

之后,对anObject进行类型判断,类型为String后,继续判断,否则直接返回false。

再对两者的长度进行判断,如果相等,继续判断,否则返回false。

两者长度相等后,再从前往后依次比较两者字符数组中的元素是否相等,全相等的后,返回true。

equals()方法一上来没有直接比较两个字符串的字符数组元素,在比较超长的字符串时,节省了大量的时间。

(2)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;
        }

比较两个字符串,可以用来排序。String类中还有一个内部类CaseInsensitiveComparator,其中也有一个compare()方法,与compareTo()方不同的是,compare()进行比较时,会忽略两个字符串的大小写。

(3)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()方法,用于计算String实例的哈希值。

哈希值相同的两个字符串不一定相同,相同的字符串的哈希值一定相同。

至此,String类重写了equals(),也重写了hashCode()方法。关于两个方法之间的关系,可以参考我的另外一篇文章【JAVA】为什么重写equals(),就必须要重写hashCode()?

(4)intern()方法

    public native String intern();

可以看得出,它是一个本地方法。

当调用此方法时,会首先在方法区中的常量池中使用equals()寻找是否存在此字符串,如果存在,直接返回此字符串的引用。如果不存在时,会首先将此字符串添加到常量池中,再返回该字符串的引用。

下面通过一个例子来说明

    package day1203;
     
    //String类的源码
    public class StringTest {
        public static void main(String[] args) {
            String a = "abc";//这个abc在常量池中创建
            String b = new String("abc");//这个abc在堆上创建
            String c = b.intern();//在常量池中寻找是否存在abc,若存在的话,直接返回它的引用。
            System.out.println(a == b);//返回false
            System.out.println(a == c);//返回true
        }
    }

这里涉及字符串的创建与存储方式,可以参考我的另外一篇文章【JAVA】字符串的创建与存储机制
五、总结

String类中还有很多有趣的操作,比如字符串的截取、匹配、替换、大小写转换、分割等操作,这里都没有涉及。这些操作确实都是经常用到的,相信大家也能够理解他们的用法,这里就不再赘述了。

如果还有一些疑问,也欢迎大家在下方评论,我一定会及时回复。
---------------------
作者:SunAlwaysOnline
来源:CSDN
原文:https://blog.csdn.net/qq_33591903/article/details/84778059
版权声明:本文为博主原创文章,转载请附上博文链接!

posted @ 2019-05-15 16:58  天涯海角路  阅读(156)  评论(0)    收藏  举报