• Java中String、StringBuilder、StringBuffer常用源码分析及比较(一):String源码分析


    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;

    其中字符数组value[]是String用来存贮字符串的容器,换句话说String是使用字符数组实现的,值得注意的是这个字符数组用到了final修饰,意味着其中的字符串一旦在构造方法中初始化将不能被修改,这也是String字符串在做拼接时,要新建很多String对象的原因;

    hash这个变量则是存贮一个String对象的hash值;

    String类型实现了Serializable序列化标记接口,所以拥有序列化ID即serialVersionUID。

    二、构造方法:

    /**
         * Initializes a newly created {@code String} object so that it represents
         * an empty character sequence.  Note that use of this constructor is
         * unnecessary since Strings are immutable.
         */
        public String() {
            this.value = new char[0];
        }
    
        /**
         * Initializes a newly created {@code String} object so that it represents
         * the same sequence of characters as the argument; in other words, the
         * newly created string is a copy of the argument string. Unless an
         * explicit copy of {@code original} is needed, use of this constructor is
         * unnecessary since Strings are immutable.
         *
         * @param  original
         *         A {@code String}
         */
        public String(String original) {
            this.value = original.value;
            this.hash = original.hash;
        }
    
        /**
         * Allocates a new {@code String} so that it represents the sequence of
         * characters currently contained in the character array argument. The
         * contents of the character array are copied; subsequent modification of
         * the character array does not affect the newly created string.
         *
         * @param  value
         *         The initial value of the string
         */
        public String(char value[]) {
            this.value = Arrays.copyOf(value, value.length);
        }
    
        /**
         * Allocates a new {@code String} that contains characters from a subarray
         * of the character array argument. The {@code offset} argument is the
         * index of the first character of the subarray and the {@code count}
         * argument specifies the length of the subarray. The contents of the
         * subarray are copied; subsequent modification of the character array does
         * not affect the newly created string.
         *
         * @param  value
         *         Array that is the source of characters
         *
         * @param  offset
         *         The initial offset
         *
         * @param  count
         *         The length
         *
         * @throws  IndexOutOfBoundsException
         *          If the {@code offset} and {@code count} arguments index
         *          characters outside the bounds of the {@code value} array
         */
        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);
        }

    其中无参构造方法,可以看到是创建了一个容量为0的char数组,可用性不高;

    String(String original)方法,则是将original直接拷贝了一份到新的String中,但别看是拷贝若用“==”去对两个String做判断,结果还是会返回false,因为两者指向的并非同一个地址,都是一个新的String对象;

    String(char value[])方法直接调用Arrays.copy方法将参数value拷贝进了String中的value,而Arrays.copy内部的调用如下,是创建了一个新的char数组,而非直接将参数value赋给String中的value,故参数的value与String中的values没有指向同一个地址;

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

    public String(char value[], int offset, int count)则是将value中的一部分(以下标offset开头,长度为count),复制给了String;

    构造方法中还有一个以整型数组为参数的构造方法,如下:

    /**
         * Allocates a new {@code String} that contains characters from a subarray
         * of the <a href="Character.html#unicode">Unicode code point</a> array
         * argument.  The {@code offset} argument is the index of the first code
         * point of the subarray and the {@code count} argument specifies the
         * length of the subarray.  The contents of the subarray are converted to
         * {@code char}s; subsequent modification of the {@code int} array does not
         * affect the newly created string.
         *
         * @param  codePoints
         *         Array that is the source of Unicode code points
         *
         * @param  offset
         *         The initial offset
         *
         * @param  count
         *         The length
         *
         * @throws  IllegalArgumentException
         *          If any invalid Unicode code point is found in {@code
         *          codePoints}
         *
         * @throws  IndexOutOfBoundsException
         *          If the {@code offset} and {@code count} arguments index
         *          characters outside the bounds of the {@code codePoints} array
         *
         * @since  1.5
         */
        public String(int[] codePoints, 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 > codePoints.length - count) {
                throw new StringIndexOutOfBoundsException(offset + count);
            }
    
            final int end = offset + count;
    
            // Pass 1: Compute precise size of char[]
            int n = count;
            for (int i = offset; i < end; i++) {
                int c = codePoints[i];
                if (Character.isBmpCodePoint(c))
                    continue;
                else if (Character.isValidCodePoint(c))
                    n++;
                else throw new IllegalArgumentException(Integer.toString(c));
            }
    
            // Pass 2: Allocate and fill in char[]
            final char[] v = new char[n];
    
            for (int i = offset, j = 0; i < end; i++, j++) {
                int c = codePoints[i];
                if (Character.isBmpCodePoint(c))
                    v[j] = (char)c;
                else
                    Character.toSurrogates(c, v, j++);
            }
    
            this.value = v;
        }

    可知:这个方法并非简单的将整型数组中的数转成字符(如1转成‘1’),而是找该整数UniCode码对应的字符,传入value数组中,其中isBmpCodePoint方法则是判定这个整数是否在一个码点之内(这个码点是UniCode码表中字符是一个字节还是两个字节的分解线,Unicode码中有一些字符是两个字节,如汉字),若不在这个范围内,自然容量要+1,否则char数组value将装不下。

    还有一些构造方法不常用,在此就不做描述;

    三、成员方法(列举常用的几个方法)

    1.indexOf方法

    /**
         * Returns the index within this string of the first occurrence of
         * the specified character. If a character with value
         * <code>ch</code> occurs in the character sequence represented by
         * this <code>String</code> object, then the index (in Unicode
         * code units) of the first such occurrence is returned. For
         * values of <code>ch</code> in the range from 0 to 0xFFFF
         * (inclusive), this is the smallest value <i>k</i> such that:
         * <blockquote><pre>
         * this.charAt(<i>k</i>) == ch
         * </pre></blockquote>
         * is true. For other values of <code>ch</code>, it is the
         * smallest value <i>k</i> such that:
         * <blockquote><pre>
         * this.codePointAt(<i>k</i>) == ch
         * </pre></blockquote>
         * is true. In either case, if no such character occurs in this
         * string, then <code>-1</code> is returned.
         *
         * @param   ch   a character (Unicode code point).
         * @return  the index of the first occurrence of the character in the
         *          character sequence represented by this object, or
         *          <code>-1</code> if the character does not occur.
         */
        public int indexOf(int ch) {
            return indexOf(ch, 0);
        }
    
        /**
         * Returns the index within this string of the first occurrence of the
         * specified character, starting the search at the specified index.
         * <p>
         * If a character with value <code>ch</code> occurs in the
         * character sequence represented by this <code>String</code>
         * object at an index no smaller than <code>fromIndex</code>, then
         * the index of the first such occurrence is returned. For values
         * of <code>ch</code> in the range from 0 to 0xFFFF (inclusive),
         * this is the smallest value <i>k</i> such that:
         * <blockquote><pre>
         * (this.charAt(<i>k</i>) == ch) && (<i>k</i> &gt;= fromIndex)
         * </pre></blockquote>
         * is true. For other values of <code>ch</code>, it is the
         * smallest value <i>k</i> such that:
         * <blockquote><pre>
         * (this.codePointAt(<i>k</i>) == ch) && (<i>k</i> &gt;= fromIndex)
         * </pre></blockquote>
         * is true. In either case, if no such character occurs in this
         * string at or after position <code>fromIndex</code>, then
         * <code>-1</code> is returned.
         *
         * <p>
         * There is no restriction on the value of <code>fromIndex</code>. If it
         * is negative, it has the same effect as if it were zero: this entire
         * string may be searched. If it is greater than the length of this
         * string, it has the same effect as if it were equal to the length of
         * this string: <code>-1</code> is returned.
         *
         * <p>All indices are specified in <code>char</code> values
         * (Unicode code units).
         *
         * @param   ch          a character (Unicode code point).
         * @param   fromIndex   the index to start the search from.
         * @return  the index of the first occurrence of the character in the
         *          character sequence represented by this object that is greater
         *          than or equal to <code>fromIndex</code>, or <code>-1</code>
         *          if the character does not occur.
         */
        public int indexOf(int ch, int fromIndex) {
            final int max = value.length;
            if (fromIndex < 0) {
                fromIndex = 0;
            } else if (fromIndex >= max) {
                // Note: fromIndex might be near -1>>>1.
                return -1;
            }
    
            if (ch < Character.MIN_SUPPLEMENTARY_CODE_POINT) {
                // handle most cases here (ch is a BMP code point or a
                // negative value (invalid code point))
                final char[] value = this.value;
                for (int i = fromIndex; i < max; i++) {
                    if (value[i] == ch) {
                        return i;
                    }
                }
                return -1;
            } else {
                return indexOfSupplementary(ch, fromIndex);
            }
        }

    由于第一个方法调用的是第二个方法,主要看第二个方法:

    其实我们从之前看到的代码就可以知道,Java的健壮性非常好,因为每一个具体的方法都有对数组是否越界、参数是否合理做了判断并处理。

    这个方法是根据传入整型根据Unicode码来查找对应字符串的,fromIndex则是表示从第几个字符开始查找,该方法同样要考虑到有些字占两个字节的情况,不过看完这个方法,可以看出,Java语言对于查找这件事情也没有太好的方法,它也只能遍历char数组来查找对应字符。

    /**
         * Returns the index within this string of the first occurrence of the
         * specified substring.
         *
         * <p>The returned index is the smallest value <i>k</i> for which:
         * <blockquote><pre>
         * this.startsWith(str, <i>k</i>)
         * </pre></blockquote>
         * If no such value of <i>k</i> exists, then {@code -1} is returned.
         *
         * @param   str   the substring to search for.
         * @return  the index of the first occurrence of the specified substring,
         *          or {@code -1} if there is no such occurrence.
         */
        public int indexOf(String str) {
            return indexOf(str, 0);
        }
    
        /**
         * Returns the index within this string of the first occurrence of the
         * specified substring, starting at the specified index.
         *
         * <p>The returned index is the smallest value <i>k</i> for which:
         * <blockquote><pre>
         * <i>k</i> &gt;= fromIndex && this.startsWith(str, <i>k</i>)
         * </pre></blockquote>
         * If no such value of <i>k</i> exists, then {@code -1} is returned.
         *
         * @param   str         the substring to search for.
         * @param   fromIndex   the index from which to start the search.
         * @return  the index of the first occurrence of the specified substring,
         *          starting at the specified index,
         *          or {@code -1} if there is no such occurrence.
         */
        public int indexOf(String str, int fromIndex) {
            return indexOf(value, 0, value.length,
                    str.value, 0, str.value.length, fromIndex);
        }
    
        /**
         * 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.
         */
        static int indexOf(char[] source, int sourceOffset, int sourceCount,
                char[] target, int targetOffset, int targetCount,
                int fromIndex) {
            if (fromIndex >= sourceCount) {
                return (targetCount == 0 ? sourceCount : -1);
            }
            if (fromIndex < 0) {
                fromIndex = 0;
            }
            if (targetCount == 0) {
                return fromIndex;
            }
    
            char first = target[targetOffset];
            int max = sourceOffset + (sourceCount - targetCount);
    
            for (int i = sourceOffset + fromIndex; i <= max; i++) {
                /* Look for first character. */
                if (source[i] != first) {
                    while (++i <= max && source[i] != first);
                }
    
                /* Found first character, now look at the rest of v2 */
                if (i <= max) {
                    int j = i + 1;
                    int end = j + targetCount - 1;
                    for (int k = targetOffset + 1; j < end && source[j]
                            == target[k]; j++, k++);
    
                    if (j == end) {
                        /* Found whole string. */
                        return i - sourceOffset;
                    }
                }
            }
            return -1;
        }

    参数为String类型的同理,不过是变成了查找匹配字符串的第一个下标,同样用的是遍历,不过在此我发现一个我原来不知道的情况,如下:

    public int indexOf(String str, int fromIndex) {
            return indexOf(value, 0, value.length,
                    str.value, 0, str.value.length, fromIndex);
        }

    str竟然可以可以直接用过‘.’语法直接得到value,value可是用private修饰的,OH!,于是我用一个Test来验证了一下,如下:

    public class StrInnerTestBean {
    
        private final char[] str;
        
        public StrInnerTestBean() {
            this.str = new char[]{'h','e','l','l','o'};
        }
        
        public static void test(StrInnerTestBean bean){
            System.out.println(bean.str);
        }
        
        public static void main(String[] args) {
            test(new StrInnerTestBean());
        }
    }

    可以看出若传入参数是该类一个对象,就可直接通过'.'语法获得private修饰的值,若不是该类的对象则不可。

    2.subString方法

    /**
         * Returns a new string that is a substring of this string. The
         * substring begins with the character at the specified index and
         * extends to the end of this string. <p>
         * Examples:
         * <blockquote><pre>
         * "unhappy".substring(2) returns "happy"
         * "Harbison".substring(3) returns "bison"
         * "emptiness".substring(9) returns "" (an empty string)
         * </pre></blockquote>
         *
         * @param      beginIndex   the beginning index, inclusive.
         * @return     the specified substring.
         * @exception  IndexOutOfBoundsException  if
         *             <code>beginIndex</code> is negative or larger than the
         *             length of this <code>String</code> object.
         */
        public String substring(int beginIndex) {
            if (beginIndex < 0) {
                throw new StringIndexOutOfBoundsException(beginIndex);
            }
            int subLen = value.length - beginIndex;
            if (subLen < 0) {
                throw new StringIndexOutOfBoundsException(subLen);
            }
            return (beginIndex == 0) ? this : new String(value, beginIndex, subLen);
        }
    
        /**
         * Returns a new string that is a substring of this string. The
         * substring begins at the specified <code>beginIndex</code> and
         * extends to the character at index <code>endIndex - 1</code>.
         * Thus the length of the substring is <code>endIndex-beginIndex</code>.
         * <p>
         * Examples:
         * <blockquote><pre>
         * "hamburger".substring(4, 8) returns "urge"
         * "smiles".substring(1, 5) returns "mile"
         * </pre></blockquote>
         *
         * @param      beginIndex   the beginning index, inclusive.
         * @param      endIndex     the ending index, exclusive.
         * @return     the specified substring.
         * @exception  IndexOutOfBoundsException  if the
         *             <code>beginIndex</code> is negative, or
         *             <code>endIndex</code> is larger than the length of
         *             this <code>String</code> object, or
         *             <code>beginIndex</code> is larger than
         *             <code>endIndex</code>.
         */
        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);
        }

    这个方法很简单,就是创建一个String将value根据下标进行截取再传入就可,但是需要注意的是若没有对String进行截取,会怎么做,还会创建一个新String,copy一份吗,从代码中可以看到,若没有截取,是直接将当前对象返回并未新建String

  • 相关阅读:
    【BZOJ1002】【FJOI2007】轮状病毒(生成树计数)
    【BZOJ1003】【ZJOI2006】物流运输
    【BZOJ1001】狼抓兔子
    【对noip结束后一个月内的总结】
    floyd原理以及求最小环
    三角形面积求法
    6、task,线程和executor间的关系
    [leetcode]Valid Sudoku
    [leetcode]Search Insert Position
    rand5()产生rand7()
  • 原文地址:https://www.cnblogs.com/tz346125264/p/7622923.html
Copyright © 2020-2023  润新知