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

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程语言 > java >内容正文

java

Java中的String为什么是不可变的?

發布時間:2023/12/14 java 28 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Java中的String为什么是不可变的? 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

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

什么是不可變對象?

? ? ? ?如果一個對象它被創建后,狀態不能改變,則這個對象被認為是不可變的。

String是如何實現其對象不可變?

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

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

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

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

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

程序例子:

public static void main(String[] args) {String str1 = "hello";// 打印str1的內存地址System.out.println("str1的內存地址:" + System.identityHashCode(str1));String str2 = "world";str1 += str2;// str1的內存地址已經改變了System.out.println("執行+=后str1的內存地址:" + System.identityHashCode(str1));System.out.println("拼接之后str1的值:" + str1);String str3 = "123";// 創建一個新的對象來保存拼接之后的值String str4 = str3.concat("456");// concat操作不會改變原來str3的值System.out.println("str3的值:" + str3);System.out.println("str4的值:" + str4);String str5 = "ABC";// replace操作不會改變原來str6的值String str6 = str5.replace("A", "B");System.out.println("str5的值:" + str5);System.out.println("str6的值:" + str6);}

運行結果:

str1的內存地址:1922154895 執行+=后str1的內存地址:883049899 拼接之后str1的值:helloworld str3的值:123 str4的值:123456 str5的值:ABC str6的值:BBC

程序分析:

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

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

concat方法源碼:

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

replace方法源碼:

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

String對象真的不可變嗎?

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

public static void main(String[] args) throws Exception {String str = "Hello World";System.out.println("修改前的str:" + str);System.out.println("修改前的str的內存地址" + System.identityHashCode(str));// 獲取String類中的value字段Field valueField = String.class.getDeclaredField("value");// 改變value屬性的訪問權限valueField.setAccessible(true);// 獲取str對象上value屬性的值char[] value = (char[]) valueField.get(str);// 改變value所引用的數組中的字符value[3] = '?';System.out.println("修改后的str:" + str);System.out.println("修改前的str的內存地址" + System.identityHashCode(str));}

運行結果:

修改前的str:Hello World 修改前的str的內存地址1922154895 修改后的str:Hel?o World 修改前的str的內存地址1922154895

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

總結

以上是生活随笔為你收集整理的Java中的String为什么是不可变的?的全部內容,希望文章能夠幫你解決所遇到的問題。

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