解析Java中的String、StringBuilder、StringBuffer類(一)

小說:哪里有賣秋楓種子的公司?作者:平扁平徒更新時間:2019-05-27字數:88200

解析Java中的String、StringBuilder、StringBuffer類(一)


引言

String 類及其相關的StringBuilder、StringBuffer 類在 Java 中的使用相當的多,在各個公司的面試中也是必不可少的。因此,在本周,我打算花費一些時間來認真的研讀一下 String、StringBuilder、StringBuffer類 的相關代碼。

String的不可變性

這個特性是 String 相當重要的一個特性,為了深入理解,我直接貼上其源代碼

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

通過以上幾個方法的代碼,我們可以得出以下的結論:

  1. String 對象時不可變的。所謂不可變的意思是說我們使用的很多方法來對字符串進行修改,如以下所示:
public static void main(String[] args) {
        String str = "test";
        str = str + "a";
        System.out.println(str);//testa
        str += "b";
        System.out.println(str);//testab
    }

諸如上面的 + 號和 concat, replace 等看起來會改變 String 值的方法,其最終都是創建了一個全新的 String 對象,用來包含修改后的字符串內容。str 最先指向的對象 "test" 一直呆在原物理位置上。各個方法操作的其實是復制的一份引用,返回的是一個新的對象,以上例子的原 "test" 還在原始處。

一些誤區:String 的不可變性并不是因為下面的語句

 private final char value[];

final 在引用類型中,只是確保了不能指向其它引用,而不能確保引用的更改。value 是 private 的, 雖然 String 沒有提供更改value的方法,但通過反射可將其更改。

String的 + 與 += 符號

眾所周知, C++ 是可以重載操作符的, 但 Java 并不允許程序員對操作符進行重載,而String的 + 與 += 符號卻違反了這個規則。我們都知道,Java 中的這兩個操作符都是對字符串進行拼接,看一下以下的代碼:

class Test{
    public static void main(String[] args){
        String str = "a";
        str +="hello" + "world" + "!";
        System.out.println(str);//ahelloworld!
        
        String pStr = "a" + str + "b";
        System.out.println(pStr);//aahelloworld!b
    }
}

如符按照我們理解的 String 的不可變性,那么在多次進行 + 操作時,應該會在最終的結果之前生成多個中間的文件,那么事實真的是這樣子嗎?如果是這樣子想一下就知道效率和性能有多糟糕了。我們對代碼進行反編譯。

javap -c Test

其產生了如下的 JVM 字節碼:
字節碼
我們可以看到,在以上的代碼中, 編譯器為我們自動的引入了 java.lang.StringBuilder 類, 并使用了該類的 append(String str) 這個方法, 最終使用 toString() 產生 String 字符串并進行了賦值。

「循環」中拼接字符串不要使用 String

那么,我們是不是可以愉快的使用 + 和 += 這兩個操作符了呢? NO! 編譯器所能做的也是有限的。

public class Test {
    public static void main(String[] args) {
        String s = "";
        long start = System.currentTimeMillis();
        for (int i = 0; i <100000 ; i++) {
            s += "a";
        }
        long end = System.currentTimeMillis();
        System.out.println("String time:"+ (end - start));

        StringBuilder sb = new StringBuilder("");
        long start2 = System.currentTimeMillis();
        for (int i = 0; i <100000 ; i++) {
           sb.append("a");
        }
        long end2 = System.currentTimeMillis();
        System.out.println("StringBuilder time:"+ (end2 - start2));
    }
}

在以上的代碼中, 輸出是這樣子的, 我還去掉了中間裝載類所花費的時間:

String time:2573
StringBuilder time:5

為什么差距這么大呢?

同樣的,我們對這個文件進行反編譯,第一個循環的字節碼如下:
第一個循環字節碼
可以看出 10 到 40 就是我們的循環體了,在該循環體中,有一個 new 的操作,這意味著每次進行循環時,都會創建一個 StringBuilder 的對象,每次都使用一次 toString() 方法。而這些過程都是相當的影響性能的。

而第二個循環的字節碼如下:
這里寫圖片描述
從以上我們可以看出,循環是從 95 到 113 行,而這個過程中,始終只有一個 StringBuilder 的對象, 也就是說它沒有產生新的對象, 可想而知兩個之間的性能是怎么產生差異的了。

因此,「循環」中拼接字符串不要使用 String, 這個在寫 toString() 方法時可能會遇到。

好,今天我們就分析到這里,下篇文章再會。

另,轉載請注明出處。

當前文章:http://www.hfcxdn.com/content/201811/23/content_88041.html

發布時間:2019-05-27 11:07:53

廣東有種植金葉榆的基地嗎? 2016下半年雪松價格表最新報價_購苗必讀! 屋頂綠化怎樣緩解熱島效應? 紫藤到底能長多長? 三公分樹狀月季今年什么價格? 2.8米高北海道黃楊今年什么價格? 衛矛球是不是常綠的? 【經驗分享】適合最重要,不同地方早熟禾播種量并不一樣 哪里有賣蜂室花種子的? 哪里有賣茺蔚子種子的?

哪里有供應欒樹的種植基地? 棕櫚樹移栽成活率高嗎? 紫荊花小樹苗多少錢一棵? 求購八角金盤苗木,來華東大型產區購買 咖啡種子幾月播種最好?  格?;ㄊ窃谑裁磿r候播種的? 草花觀賞期最好是什么時候? 今年法桐種子賣多少錢一斤? 遼寧哪里有賣中華文母種子的? 盆栽紅梅如何制作及養護管理技巧 4公分的竹苗哪里最多?

編輯:王丁

我要說兩句: (0人參與)

發布
捕鱼达人之深海狩猎