文档章节

【Java部分源码分析之lang篇】2.String

gongwilliam
 gongwilliam
发布于 2017/07/10 11:29
字数 2455
阅读 17
收藏 0

在Java开发中,String是我们经常使用的类,使用频率应该是最高的之一。对于这样频繁使用的java类难道你就没有冲动去了解它吗?!好的,打开java源码,进入lang包,找到String类,你会发现这个类很庞大,3100+行(当然包括了注释),可以到我的oscgit上查看,你会发现String超过一半的函数都是重载函数。所以我们不能把所有代码都贴出来了,得选一些精华出来。下面是我选的关于String类的关键性函数。talk is cheap,show you the code!

package java.lang;

import java.io.ObjectStreamField;
import java.io.UnsupportedEncodingException;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Comparator;
import java.util.Formatter;
import java.util.Locale;
import java.util.Objects;
import java.util.StringJoiner;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.regex.PatternSyntaxException;


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;

    public String() {
        this.value = "".value;
    }

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

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

   
    public String(char value[], int offset, int count) {
        if (offset < 0) {
            throw new StringIndexOutOfBoundsException(offset);
        }
        if (count <= 0) {
            if (count < 0) {
                throw new StringIndexOutOfBoundsException(count);
            }
            if (offset <= value.length) {
                this.value = "".value;
                return;
            }
        }
        // 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);
    }
    
    public String(StringBuffer buffer) {
        synchronized(buffer) {
            this.value = Arrays.copyOf(buffer.getValue(), buffer.length());
        }
    }
    
    public String(StringBuilder builder) {
        this.value = Arrays.copyOf(builder.getValue(), builder.length());
    }
   
    public int length() {
        return value.length;
    }

   
    public boolean isEmpty() {
        return value.length == 0;
    }

    
    public char charAt(int index) {
        if ((index < 0) || (index >= value.length)) {
            throw new StringIndexOutOfBoundsException(index);
        }
        return value[index];
    }
  
    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;
    }

    private boolean nonSyncContentEquals(AbstractStringBuilder sb) {
        char v1[] = value;
        char v2[] = sb.getValue();
        int n = v1.length;
        if (n != sb.length()) {
            return false;
        }
        for (int i = 0; i < n; i++) {
            if (v1[i] != v2[i]) {
                return false;
            }
        }
        return true;
    }

    
    public boolean contentEquals(CharSequence cs) {
        // Argument is a StringBuffer, StringBuilder
        if (cs instanceof AbstractStringBuilder) {
            if (cs instanceof StringBuffer) {
                synchronized(cs) {
                   return nonSyncContentEquals((AbstractStringBuilder)cs);
                }
            } else {
                return nonSyncContentEquals((AbstractStringBuilder)cs);
            }
        }
        // Argument is a String
        if (cs instanceof String) {
            return equals(cs);
        }
        // Argument is a generic CharSequence
        char v1[] = value;
        int n = v1.length;
        if (n != cs.length()) {
            return false;
        }
        for (int i = 0; i < n; i++) {
            if (v1[i] != cs.charAt(i)) {
                return false;
            }
        }
        return true;
    }

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

    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;
    }
    
    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 String concat(String str) {
        int otherLen = str.length();
        if (otherLen == 0) {
            return this;
        }
        int len = value.length;
        char buf[] = Arrays.copyOf(value, len + otherLen);
        str.getChars(buf, len);
        return new String(buf, true);
    }

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

            while (++i < len) {
                if (val[i] == oldChar) {
                    break;
                }
            }
            if (i < len) {
                char buf[] = new char[len];
                for (int j = 0; j < i; j++) {
                    buf[j] = val[j];
                }
                while (i < len) {
                    char c = val[i];
                    buf[i] = (c == oldChar) ? newChar : c;
                    i++;
                }
                return new String(buf, true);
            }
        }
        return this;
    }
 
    public boolean contains(CharSequence s) {
        return indexOf(s.toString()) > -1;
    }
   
    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.value.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;
            boolean limited = limit > 0;
            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);
                    list.add(substring(off, value.length));
                    off = value.length;
                    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, value.length));

            // Construct result
            int resultSize = list.size();
            if (limit == 0) {
                while (resultSize > 0 && list.get(resultSize - 1).length() == 0) {
                    resultSize--;
                }
            }
            String[] result = new String[resultSize];
            return list.subList(0, resultSize).toArray(result);
        }
        return Pattern.compile(regex).split(this, limit);
    }

   
    public String[] split(String regex) {
        return split(regex, 0);
    }

    
    public static String join(CharSequence delimiter, CharSequence... elements) {
        Objects.requireNonNull(delimiter);
        Objects.requireNonNull(elements);
        // Number of elements not likely worth Arrays.stream overhead.
        StringJoiner joiner = new StringJoiner(delimiter);
        for (CharSequence cs: elements) {
            joiner.add(cs);
        }
        return joiner.toString();
    }

    
    public static String join(CharSequence delimiter,
            Iterable<? extends CharSequence> elements) {
        Objects.requireNonNull(delimiter);
        Objects.requireNonNull(elements);
        StringJoiner joiner = new StringJoiner(delimiter);
        for (CharSequence cs: elements) {
            joiner.add(cs);
        }
        return joiner.toString();
    }

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

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

    
    public String toString() {
        return this;
    }

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

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

    
    public static String valueOf(Object obj) {
        return (obj == null) ? "null" : obj.toString();
    }

  
    public static String valueOf(char data[]) {
        return new String(data);
    }

    
    public static String valueOf(char data[], int offset, int count) {
        return new String(data, offset, count);
    }

    public static String copyValueOf(char data[], int offset, int count) {
        return new String(data, offset, count);
    }

    
    public static String copyValueOf(char data[]) {
        return new String(data);
    }

    public static String valueOf(boolean b) {
        return b ? "true" : "false";
    }

    
    public static String valueOf(char c) {
        char data[] = {c};
        return new String(data, true);
    }

    
    public static String valueOf(int i) {
        return Integer.toString(i);
    }

    
    public static String valueOf(long l) {
        return Long.toString(l);
    }

    public static String valueOf(float f) {
        return Float.toString(f);
    }

   
    public static String valueOf(double d) {
        return Double.toString(d);
    }

   
    public native String intern();
}

经过我精心的筛(shan)选(jian),我把经常用的函数留下了,接下来,我们好好的分析这些函数。

String被定义为final类,意味着它不能被继承,它是个不可变类,这样也是有一定的好处的,比如编译器针对性的代码优化、多线程环境下的安全共享等。

String的字符串内容用的char数组来保存,而这个char数据变量也是final的,意味着字符串是不可变的,所以一个String一旦被声明定义则是不可变的。那有人说了,为啥它使用的时候可以灵活的更改String的内容呢?注意,你改变的只是String对象变量的引用,JVM中有一个地方是位于方法区的运行时常量池,这里保存的都是new出来的String字符串以及被遗弃的字符串,更改String内容的本质就是:查看常量池中是否有对应的字符串,如果有则直接把变量引用到这个字符串上,若没有,则新建一个字符串扔到常量池,然后对它进行变量引用。有点喜新厌旧的味道。

大家看看下面的两个方法。

    public String(StringBuffer buffer) {
        synchronized(buffer) {
            this.value = Arrays.copyOf(buffer.getValue(), buffer.length());
        }
    }
    
    public String(StringBuilder builder) {
        this.value = Arrays.copyOf(builder.getValue(), builder.length());
    }

String提供了两个构造方法,StringBuffer与StringBuilder。它们有一点区别:StringBuffer多了一个synchronized约束,其实这也是这两者的主要区别,StringBuffer是线程安全的,而StringBuilder是线程不安全的,严谨的话几乎所有涉及StringBuffer的都会考虑线程竞争的问题。后面我会两个专题来讲这两个类,这两者几乎一致,StringBuffer每个方法扔掉synchronized约束的话,StringBuffer 几乎等于 StringBuilder。

    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重写了Object的equals方法,可以发现比较的内容是字符串。先是看看是否同一个内存地址,然后再比较一下长度,最后再比较内容,非常严谨高效的逻辑。

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

应该用的是substring返回的也是一个新的String对象,几乎所有String操作都会涉及new一个String对象,所以可以想象常量池里面的内容是多么的庞大,特别是大型的企业级项目,如果不注意合理使用String类的话,GC是非常频繁的。

    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,对于很多底层的数据拷贝这个函数用得很频繁,也非常的好用,它不是一个java方法,它是一个JNI,调用的是系统本地实现方法,可以去看一下System类。

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

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

String是如何去掉前后的空格的?就是截取前后不属于空格内容的部分。

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

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

String替换方法replace是在char数组内进行的,先定位到要替换字符的位置,然后把不需要替换的部分复制到一个新的char数组内,把要替换的部分替换成新的字符,然后利用新的char数组生成一个新的String对象返回。

    public static String valueOf(boolean b) {
        return b ? "true" : "false";
    }
    public static String valueOf(char c) {
        char data[] = {c};
        return new String(data, true);
    }
    public static String valueOf(int i) {
        return Integer.toString(i);
    }
    public static String valueOf(long l) {
        return Long.toString(l);
    }
    public static String valueOf(float f) {
        return Float.toString(f);
    }
    public static String valueOf(double d) {
        return Double.toString(d);
    }

所有的valueof操作都是利用基本数据类型包装类重写的toString方法。

接下来放点干货吧。重点来了!敲黑板了! 关于String我们会经常这样使用:

String a = "hello" + "world" + b;

在C++中,中间的+号是会定义个运载符重载的,在java中没有这个操作,那这是怎么回事呢?很简单,编译器在编译的时候会这样编译:

String a = new StringBuilder("helloworld").append(b).toString();

看明白了吗?它会利用StringBuilder来实现+号的操作。

String类内容还是挺多的,大家看源码会理解得更深。

参考:

http://www.importnew.com/7553.html http://www.cnblogs.com/lwbqqyumidi/p/4060845.html

© 著作权归作者所有

gongwilliam
粉丝 2
博文 14
码字总数 12127
作品 0
深圳
私信 提问
Groovy核心类源码讲解(终)

前两篇groovy我们从源码的角度分析了,groovy对java做了那些扩展和这些扩展的实现的原理,今天这篇文章,我们来讲解groovy源码分析中的最后一部分,来分析一下groovy中最后一部分对java做了那...

qndroid
2018/07/19
0
0
《成神之路-基础篇》Java基础知识——常用的Java工具库

本文是《成神之路系列文章》的第一篇,主要是关于JVM的一些介绍。 持续更新中 commons.lang https://commons.apache.org/proper/commons-lang/ commons.*… guava-libraries Google guava工具...

HollisChuang's Blog
2018/10/14
0
0
【目录导航】JAVA零基础进阶之路

【JAVA零基础入门系列】(已完结)导航目录 Day1 开发环境搭建 Day2 Java集成开发环境IDEA Day3 Java基本数据类型 Day4 变量与常量 Day5 Java中的运算符 Day6 Java字符串 Day7 Java输入与输出...

MFrank
2018/06/21
0
0
在Android Studio中编译谷歌自带输入法

介绍 近期开始做输入法的项目,所以必须要解决一些工程问题,在此记录下 第一篇先介绍编译篇,后续会增加java代码和native的代码分析篇 开始动手 首先,从谷歌的AOSP(Android Open Source Pr...

小包July
2016/12/03
0
0
Java 字节码结构剖析一 : 常量池

来源:拿笔小星_ , blog.csdn.net/u013096088/article/details/83047282 这篇博客开始,我打算带大家去解读一下JVM平台下的字节码文件(熟悉而又陌生的感觉)。众所周知,Class文件包含了我...

木子SMZ
2018/11/20
21
0

没有更多内容

加载失败,请刷新页面

加载更多

OpenStack 简介和几种安装方式总结

OpenStack :是一个由NASA和Rackspace合作研发并发起的,以Apache许可证授权的自由软件和开放源代码项目。项目目标是提供实施简单、可大规模扩展、丰富、标准统一的云计算管理平台。OpenSta...

小海bug
昨天
5
0
DDD(五)

1、引言 之前学习了解了DDD中实体这一概念,那么接下来需要了解的就是值对象、唯一标识。值对象,值就是数字1、2、3,字符串“1”,“2”,“3”,值时对象的特征,对象是一个事物的具体描述...

MrYuZixian
昨天
6
0
数据库中间件MyCat

什么是MyCat? 查看官网的介绍是这样说的 一个彻底开源的,面向企业应用开发的大数据库集群 支持事务、ACID、可以替代MySQL的加强版数据库 一个可以视为MySQL集群的企业级数据库,用来替代昂贵...

沉浮_
昨天
6
0
解决Mac下VSCode打开zsh乱码

1.乱码问题 iTerm2终端使用Zsh,并且配置Zsh主题,该主题主题需要安装字体来支持箭头效果,在iTerm2中设置这个字体,但是VSCode里这个箭头还是显示乱码。 iTerm2展示如下: VSCode展示如下: 2...

HelloDeveloper
昨天
7
0
常用物流快递单号查询接口种类及对接方法

目前快递查询接口有两种方式可以对接,一是和顺丰、圆通、中通、天天、韵达、德邦这些快递公司一一对接接口,二是和快递鸟这样第三方集成接口一次性对接多家常用快递。第一种耗费时间长,但是...

程序的小猿
昨天
9
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部