日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

一个小小的String问题引发的血案

發布時間:2024/10/5 编程问答 33 豆豆
生活随笔 收集整理的這篇文章主要介紹了 一个小小的String问题引发的血案 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

今天在項目中遇到了一個問題,然后我頭鐵的認為一直是bug,結果居然是String引起的,我一直沒有往String這個點上去思考,直到debug之后… …
(菜是原罪呀😣)


不知道你對String了解多少呢?
一般來說經常用過String的人都會說String是不可變的,你覺得呢?
這個不可變到底該怎么理解?是值?地址?還是其他不可變呢?
String str = "a";str = "b";System.out.println(str);

看上面這一段代碼,你認為它的答案是什么呢?
如果你認為是aaa,那就不好意思,錯了。

答案:b


如果讓很多人回答,會出現三種不同的意見

(第一種和第三種答案雖相同,但是思想不同)

第一種:b(這一種雖然答案是對的,但還不如答錯的人,因為我估計說這種答案的人沒看過String源碼

第二種:a(這一種答案雖然是錯的,但應該了解過String的源碼,知道String是不可變的

第三種:b(此b非彼b,這種是最正確的,至于為什么,下面會說)

為什么會有字符串常量池

因為字符串就是對象,我們都知道分配對象需要消耗高昂的時間和空間;另一方面,我們經常會使用字符串;所以為了減少內存開銷和提高性能,JVM處理字符串時必須要進行優化。

然后字符串常量池便出現了,當JVM運行時,系統會單獨分配一塊空間,這段空間也就是字符串常量池。

當我們創建字符串時,JVM會先檢查字符串常量池是否會有該字符串,如果有,那么就直接返回該字符串的引地址;如果沒有,則創建該字符串放到字符串常量池中并返回地址。

為什么不可變?

觀看String底層代碼可以看出由于數組使用了final關鍵字進行修飾,導致該String類的不可變性。

public final class Stringimplements java.io.Serializable, Comparable<String>, CharSequence {private final char value[];

那么由于String的不可變性,那么常量池中肯定不會出現相同的字符串。

不可變的是什么?

理解了字符串常量池這個應該就很好分析了。

String str = “a”;字符串常量池沒有相應的字符串,所以會在字符串常量池中創建該字符串,然后將地址返回給str

str = “b”;這一步會發生什么呢?由于String的不可變性,那么a是不可能改成b的;所以此時是str這個地址發生了變化。首先會創建b這個字符串,然后將地址返回給str,所以此時str指向了b;a還是a,還是那片空間,沒有任何變化。

簡單了說,變的只是引用,和字符串本身的那片空間沒有任何半毛錢關系

從String中的方法探討不可變性

字符串的截取,返回一個新的對象

public String substring(int beginIndex) {if (beginIndex < 0) {throw new StringIndexOutOfBoundsException(beginIndex);}int subLen = value.length - beginIndex;if (subLen < 0) {throw new StringIndexOutOfBoundsException(subLen);}//不過開始索引不為0,返回了一個新的String對象return (beginIndex == 0) ? this : new String(value, beginIndex, subLen);}

字符串的截取,返回一個新的String對象

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

字符串的拼接(拼接之后copy到一個新的String對象)

public String concat(String str) {int otherLen = str.length();if (otherLen == 0) {return this;}int len = value.length;//copychar buf[] = Arrays.copyOf(value, len + otherLen);str.getChars(buf, len);return new String(buf, true);}

這樣的方法還有很多很多,我們可以發現其中的特點,如果返回值類型是String,那么都是返回了一個新的對象。另外如果返回值是其他類型,那么在該方法中使用該字符串,依舊是先進行復制到一個新的數組中;然后再對其操作。


為什么要將String設置為不可變性

public final class Stringimplements java.io.Serializable, Comparable<String>, CharSequence {private final char value[];

從第一個類的修飾符final可以得出,該類是不可繼承的,也是為了防止其它人繼承該類之后,對其安全性造成破壞。

一般來說操作字符串有三個重要的類,分別是
String、StringBuffer、StringBuilder
區別就不再多說

舉個StringBuffer的例子

StringBuffer stringBuffer = new StringBuffer("aaa");StringBuffer stringBuffer2 = new StringBuffer("bbb");List<StringBuffer> list = new ArrayList<>();list.add(stringBuffer);list.add(stringBuffer2);StringBuffer stringBuffer3 = stringBuffer;stringBuffer3.append("bbb");System.out.println(list);

[aaabbb, bbb]

通過上面這個例子可以看出,我創建一個list容器;然后向其中加入兩個StringBuffer對象,通過第三個StringBuffer引用改變了容器中的內容,這是不是破壞了安全性。

再看下面這個例子

String str = new String("aaa");String str2 = new String("bbb");List<String> list = new ArrayList<>();list.add(str);list.add(str2);String str3 = str;str3 += "bbb";System.out.println(list);

[aaa, bbb]

這個想必都明白,由于String不可變,所以對list容器中的內容無法修改
這一步 str3 += “bbb” 表示創建了一個新的String對象。

總結

以上是生活随笔為你收集整理的一个小小的String问题引发的血案的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。