String類源碼解析

一、繼承體系

String是不可變類,所謂不可變類,就是創(chuàng)建該類的實例后,該實例的屬性是不可改變的。同時String類被final修飾,不可被繼承。


image.png

二、成員變量

    // 存儲字符串的字符數(shù)組,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類型的數(shù)據(jù),返回假
    if (anObject instanceof String) {
        String anotherString = (String) anObject;
        int n = value.length;
        //如果char數(shù)組長度不相等,返回假
        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方法經(jīng)常用得到,它用來判斷兩個對象從實際意義上是否相等,String對象判斷規(guī)則:

  1. 內(nèi)存地址相同,則為真。
  2. 如果對象類型不是String類型,則為假。否則繼續(xù)判斷。
  3. 如果對象長度不相等,則為假。否則繼續(xù)判斷。
  4. 從后往前,判斷String類中char數(shù)組value的單個字符是否相等,有不相等則為假。如果一直相等直到第一個數(shù),則返回真。
    注:若被比較的入?yún)ο鬄榭眨瑒t會被報NEP,所以使用該方法需要讓調(diào)用者和入?yún)⒌腟tring對象都不為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:同樣的入?yún)⒑驼{(diào)用者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維護了一個字符串常量池(字符串內(nèi)部列表)

JAVA中所有的對象都存放在堆里面,包括String對象。字符串常量保存在JAVA的.class文件的常量池中,在編譯期就確定好了。虛擬機為每個被裝載的類型維護一個常量池。常量池就是該類型所用常量的一個有序集合,包括直接常量(string、integer和float point常量)和對其他類型、字段和方法的符號引用。

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

其中字符串常量是"myString",在編譯時被存儲在常量池的某個位置。在解析階段,虛擬機發(fā)現(xiàn)字符串常量"myString",它會在一個內(nèi)部字符串常量列表中查找,如果沒有找到,那么會在堆里面創(chuàng)建一個包含字符序列[myString]的String對象s1,然后把這個字符序列和對應的String對象作為名值對( [myString], s1 )保存到內(nèi)部字符串常量列表中。如下圖所示:


image.png

如果虛擬機后面又發(fā)現(xiàn)了一個相同的字符串常量myString,它會在這個內(nèi)部字符串常量列表內(nèi)找到相同的字符序列,然后返回對應的String對象的引用。維護這個內(nèi)部列表的關鍵是任何特定的字符序列在這個列表上只出現(xiàn)一次。

例如,String s2 = "myString",運行時s2會從內(nèi)部字符串常量列表內(nèi)得到s1的返回值,所以s2和s1都指向同一個String對象。但是String對象s在堆里的一個不同位置,所以和s1不相同。

JAVA中的字符串常量可以作為String對象使用,字符串常量的字符序列本身是存放在常量池中,在字符串內(nèi)部列表中每個字符串常量的字符序列對應一個String對象,實際使用的就是這個對象。

字符串截留intern()方法

在某些上下文環(huán)境下,僅僅保留某個字符串的一份copy能夠提高內(nèi)存的使用和效率。String類的intern()方法可以截留字符串,如果String對象包含的字符序列不在字符串常量內(nèi)部列表中,那么就把這個String對象包含的字符序列和String對象的引用作為名值對保存到內(nèi)部列表中,最后intern()返回一個指向String對象本身的引用;如果String對象包含的字符序列在字符串常量內(nèi)部列表中,那么就返回列表的名值對中的對應的字符串對象引用,而String對象本身的就會被丟棄。

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

使用intern()要注意,被放到字符串內(nèi)部列表中的字符串對象是不會被垃圾回收的,生命周期和整個程序相同,所以如果使用不當會造成內(nèi)存泄露。

五、測試

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、打印結(jié)果

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

?著作權歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內(nèi)容

  • 0 概述 String表示字符串,為final類型,定義之后就不能改變 字符串緩沖區(qū)StringBuilder支持...
    AndroidTony閱讀 990評論 0 0
  • 注:源碼內(nèi)容實在太多,又想詳細分析,盡量督促自己在年前寫完~ 第一部分:String類的基本介紹。 在java.l...
    kangkaii閱讀 841評論 0 0
  • 一、Java 簡介 Java是由Sun Microsystems公司于1995年5月推出的Java面向?qū)ο蟪绦蛟O計...
    子非魚_t_閱讀 4,564評論 1 44
  • 今天正在做著填表的工作才發(fā)現(xiàn)時間都已經(jīng)七點多了,然而頭腦里沒用任何頭緒。這讓我想起之前學習的一個處理事情的時候?qū)⑹?..
    夢游世界閱讀 521評論 0 1
  • 淡入淡出效果 使用.transitionCrossDissolve 立方體旋轉(zhuǎn)效果
    ted005閱讀 519評論 0 49

友情鏈接更多精彩內(nèi)容