一区二区三区日韩精品-日韩经典一区二区三区-五月激情综合丁香婷婷-欧美精品中文字幕专区

分享

Java中的String為什么是不可變的?

 藏經(jīng)閣_蒼穹 2019-09-29

       String類是不可變類,一個String對象被創(chuàng)建以后,包含這個對象中的字符串序列是不可改變的。與其問String為什么是不可變的,還不如問String類是如何實現(xiàn)其對象不可變的。

什么是不可變對象?

       如果一個對象它被創(chuàng)建后,狀態(tài)不能改變,則這個對象被認(rèn)為是不可變的。

String是如何實現(xiàn)其對象不可變?

       首先需要補(bǔ)充一個容易混淆的知識點:當(dāng)使用final修飾基本類型變量時,不能對基本類型變量重新賦值,因此基本類型變量不能被改變。但對于引用類型變量而言,它保存的僅僅是一個引用,final只保證這個引用變量所引用的地址不會改變,即一直引用同一個對象,但這個對象完全可以發(fā)生改變。例如某個指向數(shù)組的final引用,它必須從此至終指向初始化時指向的數(shù)組,但是這個數(shù)組的內(nèi)容完全可以改變。

        我們來看一下String類的兩個主要成員變量,其中value指向的是一個字符串?dāng)?shù)組,字符串中的字符就是用這個value變量存儲起來的,并且用final修飾,也就是說value一旦賦予初始值之后,value指向的地址就不能再改變了。雖然value指向的數(shù)組是可以改變的,但是String也沒有提供相應(yīng)的方法讓我們?nèi)バ薷膙alue指向的數(shù)組的元素。然而在StringBuilder中是提供了響應(yīng)的方法讓我們?nèi)バ薷膙alue指向的數(shù)組的元素,這也是StringBuilder的字符串序列可變的原因。

  1. /** The value is used for character storage. */
  2. private final char value[];
  3. /** Cache the hash code for the string */
  4. private int hash; // Default to 0

有一些看起來String對象可變的幻覺?

      String中提供了一些看似可以改變String對象的方法,但實際上它們已經(jīng)是指向了一個新建的對象。

程序例子:

  1. public static void main(String[] args) {
  2. String str1 = "hello";
  3. // 打印str1的內(nèi)存地址
  4. System.out.println("str1的內(nèi)存地址:" + System.identityHashCode(str1));
  5. String str2 = "world";
  6. str1 += str2;
  7. // str1的內(nèi)存地址已經(jīng)改變了
  8. System.out.println("執(zhí)行+=后str1的內(nèi)存地址:" + System.identityHashCode(str1));
  9. System.out.println("拼接之后str1的值:" + str1);
  10. String str3 = "123";
  11. // 創(chuàng)建一個新的對象來保存拼接之后的值
  12. String str4 = str3.concat("456");
  13. // concat操作不會改變原來str3的值
  14. System.out.println("str3的值:" + str3);
  15. System.out.println("str4的值:" + str4);
  16. String str5 = "ABC";
  17. // replace操作不會改變原來str6的值
  18. String str6 = str5.replace("A", "B");
  19. System.out.println("str5的值:" + str5);
  20. System.out.println("str6的值:" + str6);
  21. }

運(yùn)行結(jié)果:

  1. str1的內(nèi)存地址:1922154895
  2. 執(zhí)行+=后str1的內(nèi)存地址:883049899
  3. 拼接之后str1的值:helloworld
  4. str3的值:123
  5. str4的值:123456
  6. str5的值:ABC
  7. str6的值:BBC

程序分析:

       str1+=str2實際上是執(zhí)行了str1=(new StringBuilder()).append(str2).toString();前后實際額外產(chǎn)生了一個StringBuilder與一個helloworld的字符串常量。str1執(zhí)行+=前后內(nèi)存的示意圖如下所示:

       上面使用了String類的concat與replace方法,執(zhí)行這兩個操作不會對原來的對象產(chǎn)生影響,他們會返回一個全新的對象。我們可以來看一下這兩個方法的源碼。

concat方法源碼:

  1. public String concat(String str) {
  2. int otherLen = str.length();
  3. if (otherLen == 0) {
  4. return this;
  5. }
  6. int len = value.length;
  7. char buf[] = Arrays.copyOf(value, len + otherLen);
  8. str.getChars(buf, len);
  9. return new String(buf, true);
  10. }

replace方法源碼:

  1. public String replace(char oldChar, char newChar) {
  2. if (oldChar != newChar) {
  3. int len = value.length;
  4. int i = -1;
  5. char[] val = value; /* avoid getfield opcode */
  6. while (++i < len) {
  7. if (val[i] == oldChar) {
  8. break;
  9. }
  10. }
  11. if (i < len) {
  12. char buf[] = new char[len];
  13. for (int j = 0; j < i; j++) {
  14. buf[j] = val[j];
  15. }
  16. while (i < len) {
  17. char c = val[i];
  18. buf[i] = (c == oldChar) ? newChar : c;
  19. i++;
  20. }
  21. return new String(buf, true);
  22. }
  23. }
  24. return this;
  25. }

String對象真的不可變嗎?

       雖然value是final修飾的,只是說明value不能再重新指向其他的引用。但是value指向的數(shù)組可以改變,一般情況下我們是沒有辦法訪問到這個value指向的數(shù)組的元素。But,反射,對,反射可以,牛逼吧。可以反射出String對象中的value屬性, 進(jìn)而改變通過獲得的value引用改變數(shù)組的結(jié)構(gòu)。show you the code!

  1. public static void main(String[] args) throws Exception {
  2. String str = "Hello World";
  3. System.out.println("修改前的str:" + str);
  4. System.out.println("修改前的str的內(nèi)存地址" + System.identityHashCode(str));
  5. // 獲取String類中的value字段
  6. Field valueField = String.class.getDeclaredField("value");
  7. // 改變value屬性的訪問權(quán)限
  8. valueField.setAccessible(true);
  9. // 獲取str對象上value屬性的值
  10. char[] value = (char[]) valueField.get(str);
  11. // 改變value所引用的數(shù)組中的字符
  12. value[3] = '?';
  13. System.out.println("修改后的str:" + str);
  14. System.out.println("修改前的str的內(nèi)存地址" + System.identityHashCode(str));
  15. }

運(yùn)行結(jié)果:

  1. 修改前的str:Hello World
  2. 修改前的str的內(nèi)存地址1922154895
  3. 修改后的str:Hel?o World
  4. 修改前的str的內(nèi)存地址1922154895

       可以看到str的字符串序列已經(jīng)被改變了,但是str的內(nèi)存地址還是沒有改變。有疑問?在下方留言哦。

    本站是提供個人知識管理的網(wǎng)絡(luò)存儲空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點。請注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購買等信息,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請點擊一鍵舉報。
    轉(zhuǎn)藏 分享 獻(xiàn)花(0

    0條評論

    發(fā)表

    請遵守用戶 評論公約

    類似文章 更多

    免费播放一区二区三区四区| 好吊一区二区三区在线看| 在线免费国产一区二区| 视频一区二区黄色线观看| 日本在线 一区 二区| 亚洲品质一区二区三区| 熟妇久久人妻中文字幕| 日韩黄色大片免费在线| 丰满人妻熟妇乱又乱精品古代| 日本高清不卡一二三区| 91日韩在线观看你懂的| 国产一级精品色特级色国产| 国产又粗又猛又爽色噜噜| 亚洲中文字幕人妻系列| 欧美精品一区二区三区白虎| 亚洲最新的黄色录像在线| 一区中文字幕人妻少妇| 国产对白老熟女正在播放| 国产无摭挡又爽又色又刺激| 日本人妻的诱惑在线观看| 久热久热精品视频在线观看| 久久免费精品拍拍一区二区| 少妇人妻无一区二区三区| 国产又色又爽又黄又免费| 亚洲伊人久久精品国产| 亚洲午夜福利视频在线| 九九热九九热九九热九九热| 夫妻性生活真人动作视频| 东京干男人都知道的天堂| 五月天六月激情联盟网| 亚洲国产av在线观看一区| 欧美日韩一级黄片免费观看| 超碰在线播放国产精品| 欧美精品日韩精品一区| 日韩国产亚洲欧美另类| 成人午夜免费观看视频| 国产一区二区三区丝袜不卡| 亚洲日本加勒比在线播放| 91久久精品国产一区蜜臀| 久久亚洲国产视频三级黄| 欧美日本道一区二区三区|