• String 类源码分析


    String 源码分析

    String 类代表字符序列,Java 中所有的字符串字面量都作为此类的实例。
    String 对象是不可变的,它们的值在创建之后就不能改变,因此 String 是线程安全的。
    
    • String 的主要属性
        /**
         *  用于存储字符的字节数组
         */
        @Stable
        private final byte[] value;
    
        /**
         *  用于对 value 字节数组进行编码的编码标识,支持 LATIN1、UTF16
         */
        private final byte coder;
    
        /** 缓存的哈希值 */
        private int hash; // Default to 0
    
        /** use serialVersionUID from JDK 1.0.2 for interoperability */
        private static final long serialVersionUID = -6849794470754667710L;
    
        /**
         *  如果字符串压缩被禁用,则总是使用 UTF16 编码 {@code value} 字节数组。
         *
         *  是否启用字符串压缩,默认为 true
         */
        static final boolean COMPACT_STRINGS;
    
        static {
            COMPACT_STRINGS = true;
        }
    
        @Native static final byte LATIN1 = 0;
        @Native static final byte UTF16  = 1;
    
    • String 实例化
        /**
         *  创建一个空字符串
         */
        public String() {
            value = "".value;
            coder = "".coder;
        }
    
        /**
         *  基于目标字符数组创建字符串对象
         *
         * @param value 目标字符数组
         */
        public String(char value[]) {
            this(value, 0, value.length, null);
        }
    
        /**
         *  基于目标字符数组的子数组创建字符串对象
         *
         * @param  value    源目标字符数组
         * @param  offset   起始字符索引,从 0 开始   
         * @param  count    连续截取的字符个数
         */
        public String(char value[], int offset, int count) {
            this(value, offset, count, rangeCheck(value, offset, count));
        }
    
        /**
         *  基于指定字节数组的子数组创建字符串实例
         *
         * @param  bytes    需要解码的字节数组
         * @param  offset   起始字节索引
         * @param  length   截取的元素个数
         * @param  charsetName  字符集名称
         */
        public String(byte bytes[], int offset, int length, String charsetName)
                throws UnsupportedEncodingException {
            if (charsetName == null) {
                throw new NullPointerException("charsetName");
            }
            checkBoundsOffCount(offset, length, bytes.length);
            // 使用目标字符集解码字节数组
            final StringCoding.Result ret =
                    StringCoding.decode(charsetName, bytes, offset, length);
            value = ret.value;
            coder = ret.coder;
        }
    
        /**
         *  基于指定字节数组的子数组创建字符串实例
         *
         * @param  bytes    需要解码的字节数组
         * @param  offset   起始字节索引
         * @param  length   截取的元素个数
         * @param  Charset  字符集
         */
        public String(byte bytes[], int offset, int length, Charset charset) {
            if (charset == null) {
                throw new NullPointerException("charset");
            }
            checkBoundsOffCount(offset, length, bytes.length);
            final StringCoding.Result ret =
                    StringCoding.decode(charset, bytes, offset, length);
            value = ret.value;
            coder = ret.coder;
        }
    
        /**
         *  基于目标字节数组创建字符串实例。
         *
         * @param  bytes    需要解码的字节数组
         * @param  charsetName  字符集名称
         */
        public String(byte bytes[], String charsetName)
                throws UnsupportedEncodingException {
            this(bytes, 0, bytes.length, charsetName);
        }
    
        /**
         *  基于目标字节数组创建字符串实例。
         *
         * @param  bytes    需要解码的字节数组
         * @param  Charset  字符集
         */
        public String(byte bytes[], Charset charset) {
            this(bytes, 0, bytes.length, charset);
        }
    
        /**
         *  使用平台默认的字符集解码字节数组,创建字符实例
         *
         * @param  bytes    目标字节数组
         */
        public String(byte[] bytes) {
            this(bytes, 0, bytes.length);
        }
    
    • 返回字符串的长度
        /**
         *  返回字符串的长度
         */
        @Override
        public int length() {
            return value.length >> coder();
        }
    
    • 字符串是否为空
    
        /**
         *  字符串是否为空
         */
        public boolean isEmpty() {
            return value.length == 0;
        }
    
    • 获取指定索引处的字符
        /**
         *  获取指定索引处的字符 
         *
         * @param index 目标索引
         */
        @Override
        public char charAt(int index) {
            if (isLatin1()) {
                return StringLatin1.charAt(value, index);
            } else {
                return StringUTF16.charAt(value, index);
            }
        }
    
    • 读取指定索引处的目标字符的 Unicode 代码点
        /**
         *  读取指定索引处的目标字符的 Unicode 代码点
         *
         * @param index 目标索引
         * @return
         */
        public int codePointAt(int index) {
            if (isLatin1()) {
                checkIndex(index, value.length);
                return value[index] & 0xff;
            }
            // UTF16 编码的代码点可能占用多个字节
            final int length = value.length >> 1;
                checkIndex(index, length);
                return StringUTF16.codePointAt(value, index, length);
        }
    
    • 使用指定的字符集将此字符串编码为字节数组并返回
        /**
         *  使用指定的字符集将此字符串编码为字节数组并返回。
         *
         * @param  charsetName  目标字符集名称
         */
        public byte[] getBytes(String charsetName)
                throws UnsupportedEncodingException {
            if (charsetName == null) {
                throw new NullPointerException();
            }
            return StringCoding.encode(charsetName, coder(), value);
        }
    
        /**
         *  使用指定的字符集将此字符串编码为字节数组并返回。
         *
         * @param  Charset  目标字符集
         */
        public byte[] getBytes(Charset charset) {
            if (charset == null) {
                throw new NullPointerException();
            }
            return StringCoding.encode(charset, coder(), value);
        }    
    
    • 使用平台默认的字符集将此字符串编码为字节数组并返回
        /**
         *  使用平台默认的字符集将此字符串编码为字节数组并返回。
         */
        public byte[] getBytes() {
            return StringCoding.encode(coder(), value);
        }
    
    • 此字符串的内容是否和目标字符串一致
        /**
         *  此字符串的内容是否和目标字符串一致
         */
        public boolean contentEquals(CharSequence cs) {
            // 参数是一个 StringBuffer, StringBuilder
            if (cs instanceof AbstractStringBuilder) {
                if (cs instanceof StringBuffer) {
                    synchronized(cs) {
                        return nonSyncContentEquals((AbstractStringBuilder)cs);
                    }
                } else {
                    return nonSyncContentEquals((AbstractStringBuilder)cs);
                }
            }
            // 参数是一个 String
            if (cs instanceof String) {
                return equals(cs);
            }
            // 参数是一个普通的字符序列 CharSequence
            final int n = cs.length();
            if (n != length()) {
                return false;
            }
            final byte[] val = value;
            if (isLatin1()) {
                for (int i = 0; i < n; i++) {
                    if ((val[i] & 0xff) != cs.charAt(i)) {
                        return false;
                    }
                }
            } else {
                if (!StringUTF16.contentEquals(val, cs, n)) {
                    return false;
                }
            }
            return true;
        }
    
    • 将此字符串和目标字符串进行比较,忽略大小写
        /**
         *  将此字符串和目标字符串进行比较,忽略大小写
         */
        public boolean equalsIgnoreCase(String anotherString) {
            // 先比较长度,再比较内容
            return this == anotherString ? true
                    : anotherString != null
                    && anotherString.length() == length()
                    && regionMatches(true, 0, anotherString, 0, length());
        }
    
    • 此字符串指定索引开始的子字符串是否以 prefix 开头
        /**
         *  此字符串指定索引开始的子字符串是否以 prefix 开头
         *
         * @param   prefix  前缀
         * @param   toffset 起始索引
         */
        public boolean startsWith(String prefix, int toffset) {
            // Note: toffset might be near -1>>>1.
            if (toffset < 0 || toffset > length() - prefix.length()) {
                return false;
            }
            final byte ta[] = value;
            final byte pa[] = prefix.value;
            int po = 0;
            final int pc = pa.length;
            if (coder() == prefix.coder()) {
                int to = isLatin1() ? toffset : toffset << 1;
                while (po < pc) {
                    if (ta[to++] != pa[po++]) {
                        return false;
                    }
                }
            } else {
                if (isLatin1()) {  // && pcoder == UTF16
                    return false;
                }
                // coder == UTF16 && pcoder == LATIN1)
                while (po < pc) {
                    if (StringUTF16.getChar(ta, toffset++) != (pa[po++] & 0xff)) {
                        return false;
                    }
                }
            }
            return true;
        }
    
    • 此字符串是否以指定的前缀开头
        /**
         *  此字符串是否以指定的前缀开头
         *
         * @param   prefix  目标前缀
         */
        public boolean startsWith(String prefix) {
            return startsWith(prefix, 0);
        }
    
    • 此字符串是否以指定的后缀结尾
        /**
         *  此字符串是否以指定的后缀结尾
         *
         * @param suffix    目标后缀
         */
        public boolean endsWith(String suffix) {
            return startsWith(suffix, length() - suffix.length());
        }
    
    • 指定字符在此字符串中第一次出现的索引
        /**
         *  指定字符在此字符串中第一次出现的索引
         *
         * @param ch    目标字符
         */
        public int indexOf(int ch) {
            return indexOf(ch, 0);
        }
    
        /**
         *  从 fromIndex 开始,指定字符在此字符串中第一次出现的索引
         *
         * @param ch    目标字符
         * @param fromIndex 查找的起始索引
         */
        public int indexOf(int ch, int fromIndex) {
            return isLatin1() ? StringLatin1.indexOf(value, ch, fromIndex)
                    : StringUTF16.indexOf(value, ch, fromIndex);
        }
    
    • 目标字符在此字符串中最后一次出现的索引
        /**
         *  目标字符在此字符串中最后一次出现的索引
         *
         * @param ch    目标字符
         */
        public int lastIndexOf(int ch) {
            return lastIndexOf(ch, length() - 1);
        }
    
        /**
         *  从 fromIndex 开始,目标字符在此字符串中最后一次出现的索引
         *
         * @param ch    目标字符
         * @param fromIndex 查找的起始索引
         */
        public int lastIndexOf(int ch, int fromIndex) {
            return isLatin1() ? StringLatin1.lastIndexOf(value, ch, fromIndex)
                    : StringUTF16.lastIndexOf(value, ch, fromIndex);
        }
    
    • 目标字符串 str 在此字符串中的起始索引
        /**
         *  目标字符串 str 在此字符串中的起始索引
         *
         * @param str   目标字符串
         */
        public int indexOf(String str) {
            if (coder() == str.coder()) {
                return isLatin1() ? StringLatin1.indexOf(value, str.value)
                        : StringUTF16.indexOf(value, str.value);
            }
            if (coder() == LATIN1) {  // str.coder == UTF16
                return -1;
            }
            return StringUTF16.indexOfLatin1(value, str.value);
        }
    
        /**
         *  从起始索引 fromIndex 开始,目标字符串 str 在此字符串中的起始索引
         *
         * @param str   目标字符串
         * @param fromIndex 查找的起始索引
         */
        public int indexOf(String str, int fromIndex) {
            return indexOf(value, coder(), length(), str, fromIndex);
        }
    
    • 目标字符串 str 在此字符串中最后一次出现的索引
        /**
         *  目标字符串 str 在此字符串中最后一次出现的索引
         *
         * @param str   目标字符串
         */
        public int lastIndexOf(String str) {
            return lastIndexOf(str, length());
        }
    
        /**
         *  从起始索引 fromIndex 开始,目标字符串 str 在此字符串中最后一次出现的索引
         *
         * @param str   目标字符串
         * @param fromIndex 查找的起始索引
         */
        public int lastIndexOf(String str, int fromIndex) {
            return lastIndexOf(value, coder(), length(), str, fromIndex);
        }
    
    • 截取此字符串指定索引 beginIndex 处开始到尾部的子字符串
        /**
         *  截取此字符串指定索引 beginIndex 处开始到尾部的子字符串
         *
         * @param beginIndex    截取的起始索引
         */
        public String substring(int beginIndex) {
            // 1)起始索引为负数
            if (beginIndex < 0) {
                throw new StringIndexOutOfBoundsException(beginIndex);
            }
            final int subLen = length() - beginIndex;
            // 2)起始索引超出最大索引
            if (subLen < 0) {
                throw new StringIndexOutOfBoundsException(subLen);
            }
            // 3)起始索引为 0
            if (beginIndex == 0) {
                return this;
            }
            // 4)截取子字符串
            return isLatin1() ? StringLatin1.newString(value, beginIndex, subLen)
                    : StringUTF16.newString(value, beginIndex, subLen);
        }
    
    • 截取起始索引和结束索引之间的子字符串,包括起始索引,但不包括结束索引
        /**
         *  截取起始索引 beginIndex 和结束索引 endIndex 之间的子字符串,包括起始索引,但不包括结束索引
         *
         * @param      beginIndex   起始索引【包含】
         * @param      endIndex 结束索引【不包含】
         */
        public String substring(int beginIndex, int endIndex) {
            final int length = length();
            // 1)索引非法
            checkBoundsBeginEnd(beginIndex, endIndex, length);
            final int subLen = endIndex - beginIndex;
            // 2)特殊情况
            if (beginIndex == 0 && endIndex == length) {
                return this;
            }
            // 3)截取子字符串
            return isLatin1() ? StringLatin1.newString(value, beginIndex, subLen)
                    : StringUTF16.newString(value, beginIndex, subLen);
        }
    
    • 将目标字符串 str 连接到此字符串的尾部
        /**
         *  将目标字符串 str 连接到此字符串的尾部
         *
         * @param   str 目标字符串
         */
        public String concat(String str) {
            final int olen = str.length();
            // 1)目标字符串为空
            if (olen == 0) {
                return this;
            }
            // 2)编码方式相同,则直接进行字符数组拷贝
            if (coder() == str.coder()) {
                final byte[] val = value;
                final byte[] oval = str.value;
                final int len = val.length + oval.length;
                final byte[] buf = Arrays.copyOf(val, len);
                System.arraycopy(oval, 0, buf, val.length, oval.length);
                return new String(buf, coder);
            }
            // 3)将两个字符解码并拷贝到目标字节数组中
            final int len = length();
            final byte[] buf = StringUTF16.newBytesFor(len + olen);
            getBytes(buf, 0, UTF16);
            str.getBytes(buf, len, UTF16);
            return new String(buf, UTF16);
        }
    
    • 将此字符串中所有的 oldChar 字符,替换为 newChar 字符
        /**
         *  将此字符串中所有的 oldChar 字符,替换为 newChar 字符
         *
         * @param oldChar   旧字符
         * @param newChar   新字符
         */
        public String replace(char oldChar, char newChar) {
            if (oldChar != newChar) {
                final String ret = isLatin1() ? StringLatin1.replace(value, oldChar, newChar)
                        : StringUTF16.replace(value, oldChar, newChar);
                if (ret != null) {
                    return ret;
                }
            }
            return this;
        }
    
    • 此字符串是否匹配目标正则表达式 regex
        /**
         *  此字符串是否匹配目标正则表达式 regex
         *
         * @param regex 目标正则表达式
         */
        public boolean matches(String regex) {
            return Pattern.matches(regex, this);
        }
    
    • 此字符串是否包含目标字符序列 s
        /**
         *  此字符串是否包含目标字符序列 s
         *
         * @param s 待搜索的目标字符序列
         */
        public boolean contains(CharSequence s) {
            return indexOf(s.toString()) >= 0;
        }
    
    • 将第一个匹配指定正则表达式 regex 的子字符串替换为 replacement
        /**
         *  将第一个匹配指定正则表达式 regex 的子字符串替换为  replacement
         *
         * @param regex 目标正则表达式
         * @param replacement   替换字符串
         */
        public String replaceFirst(String regex, String replacement) {
            return Pattern.compile(regex).matcher(this).replaceFirst(replacement);
        }
    
    • 将所有匹配指定正则表达式 regex 的子字符串替换为 replacement
        /**
         *  将所有匹配指定正则表达式 regex 的子字符串替换为  replacement
         *
         * @param regex 目标正则表达式
         * @param replacement   替换字符串
         */
        public String replaceAll(String regex, String replacement) {
            return Pattern.compile(regex).matcher(this).replaceAll(replacement);
        }
    
    • 将此字符串中所有的子字符串 target 替换为目标字符串 replacement
        /**
         *  将此字符串中所有的子字符串 target 替换为目标字符串 replacement
         *
         * @param target    待替换字符串
         * @param replacement   替换字符串
         */
        public String replace(CharSequence target, CharSequence replacement) {
            final String tgtStr = target.toString();
            final String replStr = replacement.toString();
            int j = indexOf(tgtStr);
            if (j < 0) {
                return this;
            }
            final int tgtLen = tgtStr.length();
            final int tgtLen1 = Math.max(tgtLen, 1);
            final int thisLen = length();
    
            final int newLenHint = thisLen - tgtLen + replStr.length();
            if (newLenHint < 0) {
                throw new OutOfMemoryError();
            }
            final StringBuilder sb = new StringBuilder(newLenHint);
            int i = 0;
            do {
                sb.append(this, i, j).append(replStr);
                i = j + tgtLen;
            } while (j < thisLen && (j = indexOf(tgtStr, j + tgtLen1)) > 0);
            return sb.append(this, i, thisLen).toString();
        }
    
    • 使用指定的正则表达式 regex 将此字符串分割为字符串数组
        /**
         *  使用指定的正则表达式 regex 将此字符串分割为字符串数组
         *
         * @param regex 目标正则表达式
         * @param limit 字符串数组的最大长度
         */
        public String[] split(String regex, int limit) {
            /* fastpath if the regex is a
             (1)one-char String and this character is not one of the
                RegEx's meta characters ".$|()[{^?*+\", or
             (2)two-char String and the first char is the backslash and
                the second is not the ascii digit or ascii letter.
             */
            char ch = 0;
            if ((regex.length() == 1 &&
                    ".$|()[{^?*+\".indexOf(ch = regex.charAt(0)) == -1 ||
                    regex.length() == 2 &&
                    regex.charAt(0) == '\' &&
                    ((ch = regex.charAt(1))-'0'|'9'-ch) < 0 &&
                    (ch-'a'|'z'-ch) < 0 &&
                    (ch-'A'|'Z'-ch) < 0) &&
                    (ch < Character.MIN_HIGH_SURROGATE ||
                            ch > Character.MAX_LOW_SURROGATE))
            {
                int off = 0;
                int next = 0;
                final boolean limited = limit > 0;
                final ArrayList<String> list = new ArrayList<>();
                while ((next = indexOf(ch, off)) != -1) {
                    if (!limited || list.size() < limit - 1) {
                        list.add(substring(off, next));
                        off = next + 1;
                    } else {    // last one
                        //assert (list.size() == limit - 1);
                        final int last = length();
                        list.add(substring(off, last));
                        off = last;
                        break;
                    }
                }
                // If no match was found, return this
                if (off == 0) {
                    return new String[]{this};
                }
    
                // Add remaining segment
                if (!limited || list.size() < limit) {
                    list.add(substring(off, length()));
                }
    
                // Construct result
                int resultSize = list.size();
                if (limit == 0) {
                    while (resultSize > 0 && list.get(resultSize - 1).length() == 0) {
                        resultSize--;
                    }
                }
                final String[] result = new String[resultSize];
                return list.subList(0, resultSize).toArray(result);
            }
            return Pattern.compile(regex).split(this, limit);
        }
    
        /**
         *  使用指定的正则表达式 regex 将此字符串分割为字符串数组
         *
         * @param regex 目标正则表达式
         */
        public String[] split(String regex) {
            return split(regex, 0);
        }
    
    • 使用指定的连接符 delimiter 顺序连接可变目标字符序列数组 elements 的所有元素
        /**
         *  使用指定的连接符 delimiter 顺序连接可变目标字符序列数组 elements 的所有元素
         */
        public static String join(CharSequence delimiter, CharSequence... elements) {
            Objects.requireNonNull(delimiter);
            Objects.requireNonNull(elements);
            // Number of elements not likely worth Arrays.stream overhead.
            final StringJoiner joiner = new StringJoiner(delimiter);
            for (final CharSequence cs: elements) {
                joiner.add(cs);
            }
            return joiner.toString();
        }
    
    • 使用指定的连接符 delimiter 顺序连接迭代器中的所有字符序列
        /**
         *  使用指定的连接符 delimiter 顺序连接迭代器中的所有字符序列
         */
        public static String join(CharSequence delimiter,
                Iterable<? extends CharSequence> elements) {
            Objects.requireNonNull(delimiter);
            Objects.requireNonNull(elements);
            final StringJoiner joiner = new StringJoiner(delimiter);
            for (final CharSequence cs: elements) {
                joiner.add(cs);
            }
            return joiner.toString();
        }
    
    • 使用指定的区域将此字符串的所有字符都转换为小写字符
        /**
         *  使用指定的区域将此字符串的所有字符都转换为小写字符
         *
         * @param locale    目标区域
         */
        public String toLowerCase(Locale locale) {
            return isLatin1() ? StringLatin1.toLowerCase(this, value, locale)
                    : StringUTF16.toLowerCase(this, value, locale);
        }
    
        /**
         *  使用默认的区域将此字符串的所有字符都转换为小写字符
         */
        public String toLowerCase() {
            return toLowerCase(Locale.getDefault());
        }
    
    • 使用指定的区域将此字符串的所有字符都转换为大写字符
        /**
         *  使用指定的区域将此字符串的所有字符都转换为大写字符
         *
         * @param locale    目标区域
         */
        public String toUpperCase(Locale locale) {
            return isLatin1() ? StringLatin1.toUpperCase(this, value, locale)
                    : StringUTF16.toUpperCase(this, value, locale);
        }
    
        /**
         *  使用默认的区域将此字符串的所有字符都转换为大写字符
         */
        public String toUpperCase() {
            return toUpperCase(Locale.getDefault());
        }
    
    • 移除此字符串前后的所有空白符
        /**
         *  移除此字符串前后的所有空白符
         */
        public String trim() {
            final String ret = isLatin1() ? StringLatin1.trim(value)
                    : StringUTF16.trim(value);
            return ret == null ? this : ret;
        }
    
    • 基于此字符串的所有字符创建整形流
        /**
         *  基于此字符串的所有字符创建整形流
         * @since 9
         */
        @Override
        public IntStream chars() {
            return StreamSupport.intStream(
                    isLatin1() ? new StringLatin1.CharsSpliterator(value, Spliterator.IMMUTABLE)
                            : new StringUTF16.CharsSpliterator(value, Spliterator.IMMUTABLE),
                            false);
        }
    
    • 基于此字符串的所有 Unicode 代码点创建整形流
        /**
         *  基于此字符串的所有 Unicode 代码点创建整形流
         * @since 9
         */
        @Override
        public IntStream codePoints() {
            return StreamSupport.intStream(
                    isLatin1() ? new StringLatin1.CharsSpliterator(value, Spliterator.IMMUTABLE)
                            : new StringUTF16.CodePointsSpliterator(value, Spliterator.IMMUTABLE),
                            false);
        }
    
    • 将此字符串转换为字符数组
        /**
         *  将此字符串转换为字符数组
         */
        public char[] toCharArray() {
            return isLatin1() ? StringLatin1.toChars(value)
                    : StringUTF16.toChars(value);
        }
    
    • 使用指定的参数列表 args 格式化目标字符串 format
        /**
         *  使用指定的参数列表 args 格式化目标字符串 format
         */
        public static String format(String format, Object... args) {
            return new Formatter().format(format, args).toString();
        }
    
    • 将目标对象 obj 转换为字符串
        /**
         *  将目标对象 obj 转换为字符串
         *
         * @param obj   目标对象
         */
        public static String valueOf(Object obj) {
            return obj == null ? "null" : obj.toString();
        }
    
    • 返回此字符串的规范表示形式,将此字符串加入由 String 类管理的字符串常量池中,所有的字符串字面量和字符串常量都缓存在字符串常量池中。
        public native String intern();
    
  • 相关阅读:
    单点登录学习的教程
    单点登录
    Linux下VI的使用
    伪分布式下的hadoop简单配置
    Linux下配置Java环境变量
    spring mvc 重新定向到一个新的Url
    LeetCode --- 字符串系列 ---“气球” 的最大数量
    http 简述
    dpr 与 移动端 1px 问题
    rem 与 vm 布局
  • 原文地址:https://www.cnblogs.com/zhuxudong/p/10447062.html
Copyright © 2020-2023  润新知