[Java]String類分析源碼閱讀

String類可以說是Java中最常使用的類了,但是String所包含的知識也是相當的多。

首先爲什麼String類一定要用equals()方法來比較字符串呢?還有new 出來的字符串和""雙印號的字符串有什麼區別呢?

看看下面這個例子

		String s1 = new String("helloworld");
		String s2 = new String("helloworld");
		String s3 = "helloworld";
		String s4 = "helloworld";
		System.out.println(s1 == s2);
		System.out.println(s1 == s3);
		System.out.println(s3 == s4);
		System.out.println("");
		System.out.println(s1.intern() == s3);
		System.out.println(s1.intern() == s3.intern());
		System.out.println("");
		String s5 = new String("hello");
		String s6 = "hello";
		System.out.println(s5 == s6);
		s5 = s5.intern();
		System.out.println(s5 == s6);

首先s1.equals(s2)是肯定是對的,等會我們分析equals的源代碼。這裏就不寫出來了

s1 == s2 肯定是false 因爲==只是比較兩個String的引用 因爲new出來 都是在堆上分配空間,所以是false

s1 == s2 不對 是因爲 s3是在運行時放進常量區  也就是方法區裏面的常量池,一個是堆上 一個是在常量區 所以false

s3 == s4 是true 是因爲 代碼經過編譯後 認爲是同一個常量  所以指向的引用地址相同。

s1.intern() == s3 和 s1.intern() == s3.intern()  返回true 是因爲intern()函數查詢常量池,如果常量池不存在,則放入常量池,然後返回引用,若是已經存在該常量則返回存在引用,所以相等

第一個s5 == s6  因爲s5指向堆而s6指向常量區所以地址不同 false

第二個s5 == s6 因爲s5變爲常量區的引用 所以true

說到這裏我想應該知道new出來的和""創建的String類型有什麼不同了嗎?一個是在編譯時候賦予引用值,一個是在運行時決定。



那麼String對象到底是可變的嗎?

一般來說String類型是不可變的,大多數情況下,String的正常操作String的子串,拼接都是返回一個新的字串。但是可以通過反射改變其值

		Field field = s1.getClass().getDeclaredField("value");
		field.setAccessible(true);
		char[] value =(char[]) field.get(s5);
		value[1]='c';
		System.out.println(s5);
		System.out.println(s6);

通過反射成功的改變了s5和s6的值。他們都是公用同一個常量引用。

輸出爲

hcllo

hcllo


還有不要濫用String類的charAt方法

爲什麼呢?char類型採用unicode編碼 即2個字節表示一個字符即 最多能表達 65536個字符 但是目前的字符遠遠超過這麼多

對於特殊字符來說,好吧 這裏我上傳的代碼段應該是非正常字符,所以被過濾導致後面的都被截斷,只能重寫,上圖了



所以對於字符處理使用codePointAt()比較於安全

源碼分析

    public int codePointAt(int index) {
        if ((index < 0) || (index >= value.length)) {
            throw new StringIndexOutOfBoundsException(index);
        }
        return Character.codePointAtImpl(value, index, value.length);
    }

這裏調用了Character的codePointAtImpl()方法轉換codePoint編碼

    static int codePointAtImpl(char[] a, int index, int limit) {
        char c1 = a[index];
        if (isHighSurrogate(c1) && ++index < limit) {
            char c2 = a[index];
            if (isLowSurrogate(c2)) {
                return toCodePoint(c1, c2);
            }
        }
        return c1;
    }

    public static int toCodePoint(char high, char low) {
        // Optimized form of:
        // return ((high - MIN_HIGH_SURROGATE) << 10)
        //         + (low - MIN_LOW_SURROGATE)
        //         + MIN_SUPPLEMENTARY_CODE_POINT;
        return ((high << 10) + low) + (MIN_SUPPLEMENTARY_CODE_POINT
                                       - (MIN_HIGH_SURROGATE << 10)
                                       - MIN_LOW_SURROGATE);
    }

通過比較適否是正常字符,是則直接返回,不是則將char[i]和char[i+1]合併成新的編碼



然後還需要注意的則是一般字符串的編碼,這種情況在網絡流的情況下遇到比較多

		String base = "why君";
		byte[] utf8s = base.getBytes("utf-8");
		for (int i = 0; i < utf8s.length; i++) {
			System.out.printf("%02x ", utf8s[i]);
		}
		System.out.println();
		byte[] gbks = base.getBytes("gbk");
		for (int i = 0; i < gbks.length; i++) {
			System.out.printf("%2x ", gbks[i]);
		}
		System.out.println();
		byte[] utf16s = base.getBytes("utf-16");
		for (int i = 0; i < utf16s.length; i++) {
			System.out.printf("%02x ", utf16s[i]);
		}
		System.out.println();
對於 why君 不同的編碼產生的效果也是不同的

                       w                       h                       y                       君

utf-8               77                      68                     79                      e5 90 9b

gbk                77                      68                     79                      be fd

utf-16(fe ff)    00 77                  00 68                00 79                54 1b

feff爲utf頭封裝

 


最後爲String的equals方法分析

    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;
    }
是否指向同一個對象引用,是true

是否String  然後比較長度是否相等 相等 則繼續逐字符比較






發佈了58 篇原創文章 · 獲贊 11 · 訪問量 4萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章