String类源码解析

一、继承体系

String是不可变类,所谓不可变类,就是创建该类的实例后,该实例的属性是不可改变的。同时String类被final修饰,不可被继承。


image.png

二、成员变量

    // 存储字符串的字符数组,final关键字修饰了,这是String是不可变类的缘由所在
    private final char value[];

    // hash码
    private int hash; // Default to 0

    // 序列码
    private static final long serialVersionUID = -6849794470754667710L;

三、常用方法

1、public boolean equals(Object anObject)方法

public boolean equals(Object anObject) {
    //如果引用的是同一个对象,返回真
    if (this == anObject) {
        return true;
    }
    //如果不是String类型的数据,返回假
    if (anObject instanceof String) {
        String anotherString = (String) anObject;
        int n = value.length;
        //如果char数组长度不相等,返回假
        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;
}

equals方法经常用得到,它用来判断两个对象从实际意义上是否相等,String对象判断规则:

  1. 内存地址相同,则为真。
  2. 如果对象类型不是String类型,则为假。否则继续判断。
  3. 如果对象长度不相等,则为假。否则继续判断。
  4. 从后往前,判断String类中char数组value的单个字符是否相等,有不相等则为假。如果一直相等直到第一个数,则返回真。
    注:若被比较的入参对象为空,则会被报NEP,所以使用该方法需要让调用者和入参的String对象都不为null才可使用。

2、int compareTo(String anotherString)方法

public int compareTo(String anotherString) {
    //自身对象字符串长度len1
    int len1 = value.length;
    //被比较对象字符串长度len2
    int len2 = anotherString.value.length;
    //取两个字符串长度的最小值lim
    int lim = Math.min(len1, len2);
    char v1[] = value;
    char v2[] = anotherString.value;

    int k = 0;
    //从value的第一个字符开始到最小长度lim处为止,如果字符不相等,返回自身(对象不相等处字符-被比较对象不相等字符)
    while (k < lim) {
        char c1 = v1[k];
        char c2 = v2[k];
        if (c1 != c2) {
            return c1 - c2;
        }
        k++;
    }
    //如果前面都相等,则返回(自身长度-被比较对象长度)
    return len1 - len2;
}

这个方法写的很巧妙,先从0开始判断字符大小。如果两个对象能比较字符的地方比较完了还相等,就直接返回自身长度减被比较对象长度,如果两个字符串长度相等,则返回的是0,巧妙地判断了三种情况。
ps:同样的入参和调用者String对象若有一个为空,则都会报NEP。

3、String concat(String str)方法

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

    // 由此可以看出String的字符串操作都会新生成一个对象返回
    return new String(buf, true);
}

四、特殊说明

1、String是不可变类

2、String不可被继承

3、String维护了一个字符串常量池(字符串内部列表)

JAVA中所有的对象都存放在堆里面,包括String对象。字符串常量保存在JAVA的.class文件的常量池中,在编译期就确定好了。虚拟机为每个被装载的类型维护一个常量池。常量池就是该类型所用常量的一个有序集合,包括直接常量(string、integer和float point常量)和对其他类型、字段和方法的符号引用。

例如:
String s = new String( "myString" );

其中字符串常量是"myString",在编译时被存储在常量池的某个位置。在解析阶段,虚拟机发现字符串常量"myString",它会在一个内部字符串常量列表中查找,如果没有找到,那么会在堆里面创建一个包含字符序列[myString]的String对象s1,然后把这个字符序列和对应的String对象作为名值对( [myString], s1 )保存到内部字符串常量列表中。如下图所示:


image.png

如果虚拟机后面又发现了一个相同的字符串常量myString,它会在这个内部字符串常量列表内找到相同的字符序列,然后返回对应的String对象的引用。维护这个内部列表的关键是任何特定的字符序列在这个列表上只出现一次。

例如,String s2 = "myString",运行时s2会从内部字符串常量列表内得到s1的返回值,所以s2和s1都指向同一个String对象。但是String对象s在堆里的一个不同位置,所以和s1不相同。

JAVA中的字符串常量可以作为String对象使用,字符串常量的字符序列本身是存放在常量池中,在字符串内部列表中每个字符串常量的字符序列对应一个String对象,实际使用的就是这个对象。

字符串截留intern()方法

在某些上下文环境下,仅仅保留某个字符串的一份copy能够提高内存的使用和效率。String类的intern()方法可以截留字符串,如果String对象包含的字符序列不在字符串常量内部列表中,那么就把这个String对象包含的字符序列和String对象的引用作为名值对保存到内部列表中,最后intern()返回一个指向String对象本身的引用;如果String对象包含的字符序列在字符串常量内部列表中,那么就返回列表的名值对中的对应的字符串对象引用,而String对象本身的就会被丢弃。

例如,s.intern()就会返回和s2相同的引用,而以前的s对象就会被垃圾回收。

使用intern()要注意,被放到字符串内部列表中的字符串对象是不会被垃圾回收的,生命周期和整个程序相同,所以如果使用不当会造成内存泄露。

五、测试

1、代码

public class StringTest {

    public static void main(String[] args) {
        test1();
    }


    /**
     * String的字符常量池及intern()方法
     */
    private static void test1(){
        String s1 = new String("ABC");
        String s2 = new String("ABC");

        System.out.println("s1 == s2 : " + (s1==s2));// false

        String s3 = "ABC";
        String s4 = "ABC";
        String s5 = "AB" + "C";

        System.out.println("s1 == s3 : " + (s1==s3));// false
        System.out.println("after s1.intern(), s1 == s3 : " + (s1.intern()==s3));// true
        System.out.println("s3 == s4 : " + (s3==s4));// true
        System.out.println("s3 == s5 : " + (s3==s5));// true

        String s6 = "ABC";
        String s7 = "AB";
        String s8 = s7 + "C";
        System.out.println("s6 == s8 : " + (s6==s8));// false
    }

}

2、打印结果

s1 == s2 : false
s1 == s3 : false
after s1.intern(), s1 == s3 : true
s3 == s4 : true
s3 == s5 : true
s6 == s8 : false

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

推荐阅读更多精彩内容

  • 0 概述 String表示字符串,为final类型,定义之后就不能改变 字符串缓冲区StringBuilder支持...
    AndroidTony阅读 938评论 0 0
  • 注:源码内容实在太多,又想详细分析,尽量督促自己在年前写完~ 第一部分:String类的基本介绍。 在java.l...
    kangkaii阅读 785评论 0 0
  • 一、Java 简介 Java是由Sun Microsystems公司于1995年5月推出的Java面向对象程序设计...
    子非鱼_t_阅读 4,201评论 1 44
  • 今天正在做着填表的工作才发现时间都已经七点多了,然而头脑里没用任何头绪。这让我想起之前学习的一个处理事情的时候将事...
    梦游世界阅读 471评论 0 1
  • 淡入淡出效果 使用.transitionCrossDissolve 立方体旋转效果
    ted005阅读 502评论 0 49