Java String 源码分析

定义

public final class String
    implements java.io.Serializable, Comparable<String>, CharSequence {

可以看出:
①、String 是 final 的(不允许被继承)
②、继承了
Serializable(可以序列化和反序列化)、
Comparable(可以进行自定义的字符串比较)、
CharSequence(一个可读序列。此接口对许多不同种类的 char 序列提供统一的只读访问。StringBuilder 和StringBuffer也实现了这个接口)接口。

属性

    /** 用于存放字符串的数组 */
    private final char value[];

由 final 可知,String 的内容一旦被初始化后,其不能被修改的(是指其内容不能被修改,其引用还是可以指向其他的内容)。
注意一点:String 是基于字符数组 char[] 实现的

    /** 缓存字符串的 hash Code */
    private int hash; // 默认为 0
    /** 因为实现了序列化接口,所有拥有序列化ID */
    private static final long serialVersionUID = -6849794470754667710L;

总结:如果你需要一个可修改的字符串,应该使用StringBuffer 或者 StringBuilder

方法

构造方法

①、无参数构造函数(会创建一个空的字符序列,可是字符串是不可变的,所以不推荐使用)

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

②、使用字符串类型的对象来初始化(这个构造方法将会产生 2 个字符串对象,除非需要 original 的显式副本,否则不要使用此构造函数)

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

④、使用字节数组来构建 String(分为以下两种)

String(byte bytes[])
String(byte bytes[], int offset, int length) // 从offset开始,长度为length 并以默认的 ISO-8859-1 编码转换成字符串

使用以下四种的话,就会使用 StringCoding.decode 方法进行解码,使用的解码的字符集就是我们指定的 charsetName 或者 charset 。

String(byte bytes[], Charset charset)
String(byte bytes[], String charsetName)
String(byte bytes[], int offset, int length, Charset charset)
String(byte bytes[], int offset, int length, String charsetName)

⑤、使用 StringBuffer 和 StringBuider 来构造 String(很少用到,因为可以直接调用 toString() 方法)

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

⑥、一个特殊的保护类型的构造方法(Java 7 提供的,直接引用指向 value[] 的地址,share 只能等于 true)

    String(char[] value, boolean share) {
        // assert share : "unshared not supported";
        this.value = value;
    }

Q:Java 7 为什么会提供这样一个方法?
A:1、性能好;2、节约空间;3、安全的

总结:如果你只需要创建一个字符串,推荐使用双引号 ""的方式,如果你需要在堆中创建一个新的对象,你可以选择构造函数的方式。

一般方法(提高效率的方法)

①、equals()方法:提高比较的效率

    public boolean equals(Object anObject) {
        // 判断是否为同一对象
        if (this == anObject) {
            return true;
        }
        // 判断 anObject 是不是 String 类型
        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;
                // 循环比较每一个 char 值
                while (n-- != 0) {
                    if (v1[i] != v2[i])
                        return false;
                    i++;
                }
                return true;
            }
        }
        return false;
    }

②、hashCode()方法:hash 地址越大,所谓的“冲突”就越少,查找起来效率也会提高。

    public int hashCode() {
        int h = hash;
        if (h == 0 && value.length > 0) {
            char val[] = value;
            // 数学公式:s[0]*31^(n-1) + s[1]*31^(n-2) + ... + s[n-1]*31^0
            for (int i = 0; i < value.length; i++) {
                h = 31 * h + val[i];
            }
            hash = h;
        }
        return h;
    }

③、substring()方法:Java 7 提供了一种新的实现方式牺牲一些性能,避免内存泄露。 Java 6 时,是同一个数组中操作。Java 7 是创建一个新的数组。

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);
        }
       // 将原来的 char[] 中的值逐一复制到新的 String 中
        return ((beginIndex == 0) && (endIndex == value.length)) ? this
                : new String(value, beginIndex, subLen);
    }

④、replaceFirst、replaceAll、replace区别:
replace 的参数是 char 和 CharSequence,即可以支持字符的替换,也支持字符串的替换;replaceAll 和 replaceFirst 的参数是 regex,即基于规则表达式的替换(如果所用的参数据不是基于规则表达式的,则与replace()替换字符串的效果一样。)

    /**
     * value 为存储当前字符串对象的字符串数组名
     * 优化了比较次数
     * @param oldChar 将被替换掉的字符
     * @param newChar 新字符
     * @return
     *//*
    public String replace(char oldChar, char newChar) {
        //如果新字符和老字符相同,直接返回当前字符串对象
        if (oldChar != newChar) {
            int len = value.length;//获取当前字符串长度
            int i = -1;
            char[] val = value; //目的:为了防止 getfield(获取指定类的实例域,并将其值压入到栈顶)这个操作码的执行
            //目的:找出将被替换的字符(oldChar)所在字符串数组中第一个字符的下标位置
            while (++i < len) {
                // ①、当找到第一个将被替换的字符时,跳出循环;i 就记录了下标位置
                // ②、如果没有找到,i==len 将不会进入下面的,直接返回当前字符串
                if (val[i] == oldChar) {
                    break;
                }
            }
            if (i < len) {
                //将第一个将被替换的字符(oldChar)之前的不需要被替换的字符赋值给新数组buf
                char buf[] = new char[len];
                for (int j = 0; j < i; j++) {
                    buf[j] = val[j];
                }
                // 从第一个将被替换的字符下标(i)开始,对字符进行替换操作
                while (i < len) {
                    char c = val[i];
                    buf[i] = (c == oldChar) ? newChar : c;
                    i++;
                }
                return new String(buf, true);
            }
        }
        return this;
    }*/ 

其他方法

自行查看源码,简单易懂。

扩展

①、String对“+”的重载:就是使用 StringBuilder 以及他的 append()、toString() 两个方法。
比如:

String str = "str";
String str1 = "this is" + str; // 等价于:String str1 = new StringBuilder("this is").append(str).toString();

②、在Java 7 中,switch 对字符串支持的实现。其实 switch 只支持一种数据类型,那就是整型,其他数据类型都是转换成整型之后再使用 switch 的。
③、String str = "abc"; 相当于 String str = String.valueOf("abc");

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,732评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,496评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,264评论 0 338
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,807评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,806评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,675评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,029评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,683评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 41,704评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,666评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,773评论 1 332
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,413评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,016评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,978评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,204评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,083评论 2 350
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,503评论 2 343

推荐阅读更多精彩内容

  • Tip:笔者马上毕业了,准备开始 Java 的进阶学习计划。于是打算先从 String 类的源码分析入手,作为后面...
    石先阅读 11,990评论 16 58
  • 1. Java基础部分 基础部分的顺序:基本语法,类相关的语法,内部类的语法,继承相关的语法,异常的语法,线程的语...
    子非鱼_t_阅读 31,577评论 18 399
  • (一)Java部分 1、列举出JAVA中6个比较常用的包【天威诚信面试题】 【参考答案】 java.lang;ja...
    独云阅读 7,066评论 0 62
  • 你說你喜歡雨 可你在雨天的時候打著傘 你說你喜歡風 可你在颳風的時候關上窗 你說你喜歡我 可你不和我共白頭 你會等...
    爱跑的濯濯阅读 204评论 0 1
  • 时间:无名的夜 地点:未知 距离:未知 工具:微信 人物:男人和女人 旁白:缘聚缘散、惜缘珍缘,愿君结下一段小善缘...
    小香凤阅读 246评论 0 1