Java源码赏析(三)初识 String 类

由于String类比较复杂,现在采用多篇幅来讲述

这一期主要从String使用的关键字,实现的接口,属性以及覆盖的方法入手。省略了大部分的字符串操作,比如split()、trim()、replace()、contains()、matches()等。

在《Java源码赏析(五)再识 String 类》中,会增加对String类中的字符串操作进行详细描述。

今后,还会在《Java源码赏析(六)Java String 三顾》中介绍StringBuffer、StringBuilder两个类。

 

/**
 * 精简的String结构,便于我们初步的理解, 省略了大部分构造方法和字符串操作
 * 使用了final关键字,说明此类是最终类,无法继承
 * 实现了序列化接口,排序接口,CharSequence接口
 */
public final class String
    implements java.io.Serializable, Comparable<String>, CharSequence {

    private final char value[];

    private int hash; // Default to 0

    private static final long serialVersionUID =         -6849794470754667710L;

    /** 指明需要实例化的字段 */
    private static final ObjectStreamField[] serialPersistentFields =
        new ObjectStreamField[0];

    /** 省略CaseInsensitiveComparator()的实现,主要是用于按ASCII码的排序规则进行排序 */
    public static final Comparator<String> CASE_INSENSITIVE_ORDER
                                         = new CaseInsensitiveComparator();

    /** 实现Comparable<String>,可用于集合类的排序功能 */
    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;
    }


    /** 覆盖hashCode(),可以用于switch以及Map,Set的查询 */
    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;
    }

    /** 覆盖父类equal(),实现了值比较 */
    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;
    }

    /** 覆盖父类toString() */
    public String toString() {
        return this;
    }

    /**
     *   实现CharSequence接口
     *   共有length(), charAt(int index), subSequence(int beginIndex, int endIndex) 等
     *   在实现subSequence()方法时使用了String中substring()方法
     */

    /** 获取字符串长度 */
    public int length() {
        return value.length;
    }

    /** 获取index位置的字符 */
    public char charAt(int index) {
        if ((index < 0) || (index >= value.length)) {
            throw new StringIndexOutOfBoundsException(index);
        }
        return value[index];
    }

    /** 获取字符串子串 */
    public CharSequence subSequence(int beginIndex, int endIndex) {
        return this.substring(beginIndex, endIndex);
    }

    
    /** 从索引位置beginIndex到endIndex处获取子串 */
    public String substring(int beginIndex, int endIndex) {
        if (beginIndex < 0) {
            throw new StringIndexOutOfBoundsException(beginIndex);
        }
        if (endIndex > value.length) {
            throw new StringIndexOutOfBoundsException(endIndex);
        }
        int subLen = endIndex - beginIndex;
        if (subLen < 0) {
            throw new StringIndexOutOfBoundsException(subLen);
        }
        return ((beginIndex == 0) && (endIndex == value.length)) ? this : new String(value, beginIndex, subLen);
    }
}
    /** 若不存在,将字符串存入常量池并返回在常量池的引用;若已存在,则直接返回字符串的引用 */
    public native String intern();

 

阅读一个类的源码,首先要定性。

1. 它是一个final的类,说明不可继承

2. value为final,说明值创建后不可更改

3. 所有看起来会修改String的方法都返回了一个新的String对象

这种字符串的不可变性的好处在于简单(不用考虑并发问题),安全(不用考虑子类、值被修改等问题),高效(由于不可变可以设计常量池的处理)等。

 

大概了解了String的属性和方法,我们回顾源码赏析一、二的内容,发现前两篇内容无一不体现在这个类中。希望大家可以结合前两篇来看待这个经典的类。

Java源码赏析(一)Object 类

Java源码赏析(二)Java常见接口


一、Object中的重写方法 equals()、 hashCode()、 toString()

二、常见的接口Serializable, Comparable的实现

 

除了上面两点之外,还实现了CharSequence这个字符串的通用接口。StringBuffer、StringBuilder也实现了此接口。

还有需要注意的一点是,字符串拥有 “操作符重载” ( +, +=)。我们可以方便的使用这个操作符进行一些处理。

比如 连接字符串

String a = "hello";
String b = ", world";
//输出 hello, world
//
System.out.println(a + b);

 

注, “+” 最后不好放在for循环之中(第一种写法创建了100个对象),而第二种只创建了1个对象

String s = new String();

for(int i=0; i<100; i++) {
    s = s + i;
}

//等效于
StringBuilder s1 = new StringBuilder();

for(int i=0; i<100; i++) {
    StringBuilder one = new StringBuilder(i);
    s1.append(one).toString();
}


//高效写法
StringBuilder s2 = new StringBuilder();

for(int i=0; i<100; i++) {
    s2 = s2.append(i);
}

 

同理,频繁使用的String也可以直接写出 String s3 = "a";,不需要new出来 String s4 = new String("a");,这样能让这个值存在于常量池里(可以当成存在缓存)。

 

posted @ 2020-09-14 16:58  Kwanwooo  阅读(187)  评论(0编辑  收藏  举报