关注Java细节 -- String类 (二)

上一篇我们一起探讨了String类的成员和构造函数,今天我们再来看看String类提供了哪些方法。
因为String类提供的方法非常多,这里我们就选其中比较有代表性的来研究一下:

1. public int length();
该方法返回String对象的成员count,从而来得到该对象的有效字符数。
需要注意的是,在String对象中可能存在垃圾数据,所以count的值与数组value的长度不一定相等。

2. public char charAt(int index);
该方法返回String对象所代表的字符序列中的第index个字符。
需要注意的是,取值的时候并不是简单的从数组value中取第index个元素,而是根据与offset联合使用,去value中的第index+offset个元素。

3. void getChars(char dst[], int dstBegin);
该方法将String对象中的字符序列按照顺序拷贝到目标数组dst中从dstBegin开始的位置。
需要注意的是,这个方法并没有做任何的越界检查。

4. public void getChars(int srcBegin, int srcEnd, char dst[], int dstBegin);
该方法和上一个方法完成相同的功能,只不过在取源数据的时候,有了限制,即取String对象中从srcBegin到srcEnd位置的数据。
该方法对srcBegin和srcEnd做了入参合法性检查,但是并没有做越界检查。

5. public byte[] getBytes(String charsetName) throws UnsupportedEncodingException;
该方法通过sharsetName指定的字符集将String对象解码成一个字节序列,并将其返回。
String类还提供了一个同名无入参的函数,该函数和本函数实现相同的功能,不同的是使用系统默认的字符集进行解码。

6. public boolean equals(Object anObject) {
        if (this == anObject) {
            return true;
        }
        if (anObject instanceof String) {
            String anotherString = (String)anObject;
            int n = count;
            if (n == anotherString.count) {
                char v1[] = value;
                char v2[] = anotherString.value;
                int i = offset;
                int j = anotherString.offset;
                while (n-- != 0) {
                    if (v1[i++] != v2[j++])
                        return false;
                }
                return true;
            }
        }
        return false;
    }
equals方法大家都知道,是用来判断两个对象是否相等的。
在String类的equals方法实现中,除了判读是否是同一对象之外,
当目标对象与当前对象不是一个对象时,如果目标对象中的字符元素与当前对象中的字符元素完全相同,那么这两个对象就是相等的,反之不等。
这里需要注意的是,两个对象比较的是他们真正所代表的字符序列,并没有对垃圾数据。
也就是说,可能存在这种情况:两个String对象中存在着不同的垃圾数据,但是他们所真正代表的字符序列是完全相同的,
因此,这两个String对象时相等的。
String类提供了另外两个函数contentEquals(StringBuffer sb)和contentEquals(CharSequence cs)来判断入参的内容是否和本身的内容相等。
其原理与equals方法相同。值得一说的一点是contentEquals(StringBuffer sb)方法内部对sb对象做了同步,因此是线程安全的。

7. public int compareTo(String anotherString) {
        int len1 = count;
        int len2 = anotherString.count;
        int n = Math.min(len1, len2);
        char v1[] = value;
        char v2[] = anotherString.value;
        int i = offset;
        int j = anotherString.offset;

        if (i == j) {
            int k = i;
            int lim = n + i;
            while (k < lim) {
                char c1 = v1[k];
                char c2 = v2[k];
                if (c1 != c2) {
                    return c1 - c2;
                }
                k++;
            }
        } else {
            while (n-- != 0) {
                char c1 = v1[i++];
                char c2 = v2[j++];
                if (c1 != c2) {
                    return c1 - c2;
                }
            }
        }
        return len1 - len2;
    }

compartTo函数大家应该都知道,用来在String对象之间比较大小,多用于排序。
String类compareTo方法中比较的规则如下:
        1. 从头开始逐一比较两个String对象所表示的char数组内容(不包含垃圾数据),当发现两个不相等的字符时,返回本对象该字符和目标对象对应字符的差
        2. 如果在步骤1的比较中完全相等,返回两个String对象的长度差

8. public boolean regionMatches(int toffset, String other, int ooffset, int len) {
        char ta[] = value;
        int to = offset + toffset;
        char pa[] = other.value;
        int po = other.offset + ooffset;
        // Note: toffset, ooffset, or len might be near -1>>>1.
        if ((ooffset < 0) || (toffset < 0) || (toffset > (long)count - len)
            || (ooffset > (long)other.count - len)) {
            return false;
        }
        while (len-- > 0) {
            if (ta[to++] != pa[po++]) {
                return false;
            }
        }
        return true;
    }
该方法对两个String对象中的一部分进行比较,判断其是否相等。
入参的定义如下:
    /**
     *
     * @param   toffset   the starting offset of the subregion in this string.
     * @param   other     the string argument.
     * @param   ooffset   the starting offset of the subregion in the string
     *                    argument.
     * @param   len       the number of characters to compare.
     * @return  true      if the specified subregion of this string
     *          exactly matches the specified subregion of the string argument;
     *          false     otherwise.
     */

 

 同时,String类还提供了另外一个函数 regionMatches(boolean ignoreCase, int toffset,String other, int ooffset, int len),这个函数与我们现在讨论的这个函数实现相同的功能,并提供了一种选择:是否在判断的时候忽略字符的大小写。

 

9. public boolean startsWith(String prefix, int toffset);
   public boolean startsWith(String prefix);

这两个函数都是用来判断String对象是否是以prefex开头的。
区别在于第二个函数是从String对象本身的offset处开始判断,而第一个函数是从String对象的offset+toffset处开始判断。
同样的,String类还提供了判断对象是否以某个字符串结尾的函数:public boolean endsWith(String suffix)。
这个函数的内部实现非常简单,直接通过调用startsWith(String prefix, int toffset)来实现:
public boolean endsWith(String suffix) {
        return startsWith(suffix, count - suffix.count);
    }

 

10. public int hashCode() {
        int h = hash;
        if (h == 0) {
            int off = offset;
            char val[] = value;
            int len = count;

            for (int i = 0; i < len; i++) {
                h = 31*h + val[off++];
            }
            hash = h;
        }
        return h;
    }

这个方法为String对象提供了获取hashCode的方法,
其算法为:s[0]*31^(n-1) + s[1]*31^(n-2) + ... + s[n-1]

 

11. public int indexOf(int ch);
    public int indexOf(int ch, int fromIndex);
    public int lastIndexOf(int ch);
    public int lastIndexOf(int ch, int fromIndex);
    public int indexOf(String str);
    public int indexOf(String str, int fromIndex);
    static int indexOf(char[] source, int sourceOffset, int sourceCount,
                       char[] target, int targetOffset, int targetCount,
                       int fromIndex);
    public int lastIndexOf(String str);
    public int lastIndexOf(String str, int fromIndex);
    static int lastIndexOf(char[] source, int sourceOffset, int sourceCount,
                           char[] target, int targetOffset, int targetCount,
                           int fromIndex);
   
这一系列函数都是实现在String对象中定位某个字符或者字符串的功能。
逻辑很简单,这里就不详细叙述了,就只是把最复杂的两个函数的入参介绍给大家列出来,供大家参考。

 

    /**
     * Code shared by String and StringBuffer to do searches. The
     * source is the character array being searched, and the target
     * is the string being searched for.
     *
     * @param   source       the characters being searched.
     * @param   sourceOffset offset of the source string.
     * @param   sourceCount  count of the source string.
     * @param   target       the characters being searched for.
     * @param   targetOffset offset of the target string.
     * @param   targetCount  count of the target string.
     * @param   fromIndex    the index to begin searching from.
     */

 

12. public String substring(int beginIndex, int endIndex) {
        if (beginIndex < 0) {
            throw new StringIndexOutOfBoundsException(beginIndex);
        }
        if (endIndex > count) {
            throw new StringIndexOutOfBoundsException(endIndex);
        }
        if (beginIndex > endIndex) {
            throw new StringIndexOutOfBoundsException(endIndex - beginIndex);
        }
        return ((beginIndex == 0) && (endIndex == count)) ? this :
            new String(offset + beginIndex, endIndex - beginIndex, value);
    }

 

substring方法是String类的一个很重要的方法,同时也是我们平时用的很多的一个方法。
这里我们看到,在对入参做了简单的校验之后,该方法简单的调用了String类的一个构造函数。
让我们再来简单的回忆一下这个构造函数:


String(int offset, int count, char value[]){
 this.value = value;
 this.offset = offset;
 this.count = count;
    }


这是一个包内私有的构造函数。通过直接共享字符数组,而不进行截取的方式来加快运行速度。

由此我们可以发现,由substring方法返回的String对象其实和源String对象中的字符数组指向的是同一块内存,
他们有着相同的数据成员。而他们对外表现的字符序列是不同的,因为他们有着不同的offset和count。

同样的,String类还提供了一个方法substring(int beginIndex),该方法的内部实现是通过调用substring(int beginIndex, int endIndex)来实现的,只不过返回的是从beginIndex开始的后面的所有字符序列。

 

13. public String concat(String str) {
        int otherLen = str.length();
        if (otherLen == 0) {
            return this;
        }
        char buf[] = new char[count + otherLen];
        getChars(0, count, buf, 0);
        str.getChars(0, otherLen, buf, count);
        return new String(0, count + otherLen, buf);
    }


concat函数实现了String对象与另外一个String对象的链接。
在函数的实现中,使用了getChars方法来实现char数组的拷贝,并最终根据新的字符数组构造了String对象。

 

14. public String replace(char oldChar, char newChar) {
        if (oldChar != newChar) {
            int len = count;
            int i = -1;
            char[] val = value; /* avoid getfield opcode */
            int off = offset;   /* avoid getfield opcode */

            while (++i < len) {
                if (val[off + i] == oldChar) {
                    break;
                }
            }
            if (i < len) {
                char buf[] = new char[len];
                for (int j = 0 ; j < i ; j++) {
                    buf[j] = val[off+j];
                }
                while (i < len) {
                    char c = val[off + i];
                    buf[i] = (c == oldChar) ? newChar : c;
                    i++;
                }
                return new String(0, len, buf);
            }
        }
        return this;
    }


replace函数将String对象中的oldChar字符全部替换成newChar字符。
请大家注意的是,当oldChar与newChar相等,或者在String对象中就根本不存在oldChar时,直接返回源String对象,
而当源对象中存在oldChar需要替换时,replace并不是在替换完成后将源String对象返回,而是构造了一个新的String对象并将其返回。

String类还提供了另外两个替换函数:replaceFirst(String regex, String replacement)和replaceAll(String regex, String replacement)。
通过这两个函数,我们可以用replacement替换掉String对象中第一个满足正则表达式regex的部分,或者替换掉所有满足正则表达式regex的部分。

 

15. public boolean matches(String regex) {
        return Pattern.matches(regex, this);
    }

 

matches函数是我们常用的一个函数,主要用来判断String对象所代表的字符串是否满足正则表达式regex所表示的规则。

我们平时在写程序的过程中,必然说在文件夹下抓取满足命名规则的文件等场景,经常会用到这个函数。

 

16. public String[] split(String regex, int limit);
    public String[] split(String regex);

 

split函数根据regex将源String对象分割成一个String对象数组并返回。
第二个函数在其内部实现中直接调用第一个函数,只不过把limit写死为0。
关于limit的定义如下:


    /**
     *
     * The limit parameter controls the number of times the
     * pattern is applied and therefore affects the length of the resulting
     * array.  If the limit n is greater than zero then the pattern
     * will be applied at most n-1 times, the array's
     * length will be no greater than n, and the array's last entry
     * will contain all input beyond the last matched delimiter.  If n
     * is non-positive then the pattern will be applied as many times as
     * possible and the array can have any length.  If n is zero then
     * the pattern will be applied as many times as possible, the array can
     * have any length, and trailing empty strings will be discarded.
     *
     */

 

17. public String toLowerCase(Locale locale);
    public String toLowerCase();
    public String toUpperCase(Locale locale);
    public String toUpperCase();

 

这一系列函数实现类对String对象中字符大小写的转换。
无参的函数其实内部实现是调用有参函数的,只不过使用的是Locale.getDefault()作为参数。

 

18. public String trim() {
        int len = count;
        int st = 0;
        int off = offset;      /* avoid getfield opcode */
        char[] val = value;    /* avoid getfield opcode */

        while ((st < len) && (val[off + st] <= ' ')) {
            st++;
        }
        while ((st < len) && (val[off + len - 1] <= ' ')) {
            len--;
        }
        return ((st > 0) || (len < count)) ? substring(st, len) : this;
    }

 

trim函数帮助String对象去掉首尾的空格字符。

 

19. public static String format(String format, Object ... args) {
        return new Formatter().format(format, args).toString();
    }
    public static String format(Locale l, String format, Object ... args) {
        return new Formatter(l).format(format, args).toString();
    }

 

这两个函数是JDK1.5新引入的函数。
他们实现了将String对象根据相应的规则进行格式化的操作。
关于format的使用方法,请参照我转载的另外一篇文章:

http://blog.csdn.net/derekjiang/archive/2009/08/18/4458869.aspx

 

20. public static String valueOf(Object obj);
    public static String valueOf(char data[]);
    public static String valueOf(char data[], int offset, int count);
    public static String copyValueOf(char data[], int offset, int count);
    public static String copyValueOf(char data[]);
    public static String valueOf(boolean b);
    public static String valueOf(char c);
    public static String valueOf(int i);
    public static String valueOf(long l);
    public static String valueOf(float f);
    public static String valueOf(double d);
   
String类提供了一系列的valueOf函数来将其他类型的数据转换成String对象。
这一系列函数都是静态函数,直接通过String类就可以调用。

 

21. public native String intern();
由String类维护一个初始为空的字符串的对象池,当intern方法被调用时,如果对象池中已经包含这一个相等的字符串对象则返回对象池中的实例,否则添加字符串到对象池并返回该字符串的引用。

举例如下:


public class TestStringIntern {
        public static void main(String[] args) {
                String a = "abc";
                String b = new String("abc");
               
                System.out.println(a == b);
                System.out.println(a == b.intern());
        }
}

 

 

 

运行该程序我们发现结果如下:

 

 


false
true

 

-----------------------------------------

 

 

至此,我们已经把String对象的大部分主要函数都做了相应的介绍。通过了解String类的函数,以及一些重要函数的实现,相信可以让我们更好的了解String类。希望对大家有帮助。

 

 

 

 

你可能感兴趣的:(java,String,正则表达式,object,equals,regex)