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

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

String为什么是不可变类型?

發(fā)布時間:2025/3/21 编程问答 18 豆豆
生活随笔 收集整理的這篇文章主要介紹了 String为什么是不可变类型? 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

什么是不可變對象?

眾所周知, 在Java中, String類是不可變的。那么到底什么是不可變的對象呢? 可以這樣認為:如果一個對象,在它創(chuàng)建完成之后,不能再改變它的狀態(tài),那么這個對象就是不可變的。不能改變狀態(tài)的意思是,不能改變對象內(nèi)的成員變量,包括基本數(shù)據(jù)類型的值不能改變,引用類型的變量不能指向其他的對象,引用類型指向的對象的狀態(tài)也不能改變。

?

區(qū)分對象和對象的引用

對于Java初學(xué)者, 對于String是不可變對象總是存有疑惑。看下面代碼:

String s = "ABCabc"; System.out.println("s = " + s); s = "123456"; System.out.println("s = " + s);

?打印結(jié)果為: s = ABCabc

s = 123456

首先創(chuàng)建一個String對象s,然后讓s的值為“ABCabc”, 然后又讓s的值為“123456”。 從打印結(jié)果可以看出,s的值確實改變了。那么怎么還說String對象是不可變的呢? 其實這里存在一個誤區(qū): s只是一個String對象的引用,并不是對象本身。對象在內(nèi)存中是一塊內(nèi)存區(qū),成員變量越多,這塊內(nèi)存區(qū)占的空間越大。引用只是一個4字節(jié)的數(shù)據(jù),里面存放了它所指向的對象的地址,通過這個地址可以訪問對象。 也就是說,s只是一個引用,它指向了一個具體的對象,當(dāng)s=“123456”; 這句代碼執(zhí)行過之后,又創(chuàng)建了一個新的對象“123456”, 而引用s重新指向了這個心的對象,原來的對象“ABCabc”還在內(nèi)存中存在,并沒有改變。內(nèi)存結(jié)構(gòu)如下圖所示:




Java和C++的一個不同點是, 在Java中不可能直接操作對象本身,所有的對象都由一個引用指向,必須通過這個引用才能訪問對象本身,包括獲取成員變量的值,改變對象的成員變量,調(diào)用對象的方法等。而在C++中存在引用,對象和指針三個東西,這三個東西都可以訪問對象。其實,Java中的引用和C++中的指針在概念上是相似的,他們都是存放的對象在內(nèi)存中的地址值,只是在Java中,引用喪失了部分靈活性,比如Java中的引用不能像C++中的指針那樣進行加減運算。?

為什么String對象是不可變的?

要理解String的不可變性,首先看一下String類中都有哪些成員變量。 在JDK1.6中,String的成員變量有以下幾個:

public final class Stringimplements java.io.Serializable, Comparable<string>, CharSequence{/** The value is used for character storage. */private final char value[];/** The offset is the first index of the storage that is used. */private final int offset;/** The count is the number of characters in the String. */private final int count;/** Cache the hash code for the string */private int hash; // Default to 0</string>

在JDK1.7中,String類做了一些改動,主要是改變了substring方法執(zhí)行時的行為,這和本文的主題不相關(guān)。JDK1.7中String類的主要成員變量就剩下了兩個:

public final class Stringimplements java.io.Serializable, Comparable<string>, CharSequence {/** 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>

由以上的代碼可以看出, 在Java中String類其實就是對字符數(shù)組(value)的封裝JDK6中, value是String封裝的數(shù)組,offset是String在這個value數(shù)組中的起始位置,count是String所占的字符的個數(shù)。在JDK7中,只有一個value變量,也就是value中的所有字符都是屬于String這個對象的。這個改變不影響本文的討論。 除此之外還有一個hash成員變量,是該String對象的哈希值的緩存,這個成員變量也和本文的討論無關(guān)。在Java中,數(shù)組也是對象(可以參考我之前的文章java中數(shù)組的特性)。 所以value也只是一個引用,它指向一個真正的數(shù)組對象。其實執(zhí)行了String s = “ABCabc”; 這句代碼之后,真正的內(nèi)存布局應(yīng)該是這樣的:?


value,offset和count這三個變量都是private的,并且沒有提供setValue, setOffset和setCount等公共方法來修改這些值,所以在String類的外部無法修改String。也就是說一旦初始化就不能修改, 并且在String類的外部不能訪問這三個成員。此外,value,offset和count這三個變量都是final的, 也就是說在String類內(nèi)部,一旦這三個值初始化了, 也不能被改變。所以可以認為String對象是不可變的了。?
那么在String中,明明存在一些方法,調(diào)用他們可以得到改變后的值。這些方法包括substring, replace, replaceAll, toLowerCase等。例如如下代碼:

String a = "ABCabc"; System.out.println("a = " + a); a = a.replace('A', 'a'); System.out.println("a = " + a);

?打印結(jié)果為: a = ABCabc

a = aBCabc

那么a的值看似改變了,其實也是同樣的誤區(qū)。再次說明, a只是一個引用, 不是真正的字符串對象,在調(diào)用a.replace('A', 'a')時, 方法內(nèi)部創(chuàng)建了一個新的String對象,并把這個新的對象重新賦給了引用a。String中replace方法的源碼可以說明問題

?

讀者可以自己查看其他方法,都是在方法內(nèi)部重新創(chuàng)建新的String對象,并且返回這個新的對象,原來的對象是不會被改變的。這也是為什么像replace, substring,toLowerCase等方法都存在返回值的原因。也是為什么像下面這樣調(diào)用不會改變對象的值:?

String ss = "123456"; System.out.println("ss = " + ss); ss.replace('1', '0'); System.out.println("ss = " + ss);

?打印結(jié)果: ss = 123456

ss = 123456
?


String對象真的不可變嗎?

從上文可知String的成員變量是private final 的,也就是初始化之后不可改變。那么在這幾個成員中, value比較特殊,因為他是一個引用變量,而不是真正的對象。value是final修飾的,也就是說final不能再指向其他數(shù)組對象,那么我能改變value指向的數(shù)組嗎? 比如將數(shù)組中的某個位置上的字符變?yōu)橄聞澗€“_”。 至少在我們自己寫的普通代碼中不能夠做到,因為我們根本不能夠訪問到這個value引用,更不能通過這個引用去修改數(shù)組。 那么用什么方式可以訪問私有成員呢? 沒錯,用反射, 可以反射出String對象中的value屬性, 進而改變通過獲得的value引用改變數(shù)組的結(jié)構(gòu)。下面是實例代碼:?

public static void testReflection() throws Exception {//創(chuàng)建字符串"Hello World", 并賦給引用sString s = "Hello World"; System.out.println("s = " + s); //Hello World//獲取String類中的value字段Field valueFieldOfString = String.class.getDeclaredField("value");//改變value屬性的訪問權(quán)限valueFieldOfString.setAccessible(true);//獲取s對象上的value屬性的值char[] value = (char[]) valueFieldOfString.get(s);//改變value所引用的數(shù)組中的第5個字符value[5] = '_';System.out.println("s = " + s); //Hello_World }

打印結(jié)果為: s = Hello World

s = Hello_World

在這個過程中,s始終引用的同一個String對象,但是再反射前后,這個String對象發(fā)生了變化, 也就是說,通過反射是可以修改所謂的“不可變”對象的。但是一般我們不這么做。這個反射的實例還可以說明一個問題:如果一個對象,他組合的其他對象的狀態(tài)是可以改變的,那么這個對象很可能不是不可變對象。例如一個Car對象,它組合了一個Wheel對象,雖然這個Wheel對象聲明成了private final 的,但是這個Wheel對象內(nèi)部的狀態(tài)可以改變, 那么就不能很好的保證Car對象不可變。

《新程序員》:云原生和全面數(shù)字化實踐50位技術(shù)專家共同創(chuàng)作,文字、視頻、音頻交互閱讀

總結(jié)

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

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。

主站蜘蛛池模板: 亚洲色图婷婷 | 欧美精品123区 | 97夜夜 | 正在播放一区 | 国产精品久久综合青草亚洲AV | av电影在线不卡 | av资源网站 | 爱插网| 久久精品视频一区二区 | 欧美成人视屏 | 四虎网址在线观看 | 国产一区二区三区毛片 | 中文字幕在线播放第一页 | 欧美在线观看a | 国产高清第一页 | 国产手机在线播放 | 奇米狠狠干 | 先锋资源中文字幕 | 九九九国产 | 国产精品视频麻豆 | 找个毛片看看 | 特级西西人体444www高清大胆 | 高h放荡受浪受bl | 日韩欧美国产一区二区 | 亚洲图片激情小说 | 色天天色综合 | 亚洲图片欧美在线看 | 亚洲三级在线看 | 久久天堂网 | 91国语对白| 日本理论视频 | 九九av在线 | 亚洲视频免费观看 | 97自拍偷拍视频 | 国产成人无码一区二区在线播放 | 玖玖色资源 | 小sao货cao死你 | 欧洲最强rapper网站直播 | 日韩中出| 日韩欧美xxxx | 精品国产露脸精彩对白 | 一级特黄毛片 | 国产精品国语 | 欧美日韩亚洲另类 | 久久看看| 伊人视屏 | 国产一级片av | 日韩精品综合 | 日韩精品乱码 | 琪琪在线视频 | 新91视频在线观看 | 公与妇乱理三级xxx www色 | 黄色的网站免费观看 | 亚洲国产欧美视频 | 欧洲三级在线 | av毛片网站 | 国产女人在线 | 美女三区 | 夏目彩春娇喘呻吟高潮迭起 | 国产99久久久国产精品成人免费 | 欧美一区亚洲 | 色老头免费视频 | 欧美性福利 | 韩国日本在线观看 | 亚洲国产一二 | 天天干狠狠 | 麻豆成人av | 亚洲综合干| 国产精品久久久久久久天堂 | 亚欧在线观看 | 在线观看成年人网站 | 蜜桃久久一区二区三区 | 在线观看欧美成人 | 欧美视频一二三区 | 男人的天堂手机在线 | 国产激情无码一区二区 | 绿帽人妻精品一区二区 | 顶级黑人搡bbw搡bbbb搡 | 性史性dvd影片农村毛片 | 午夜影音| 黄色在线免费看 | 亚洲久操 | 九九热国产视频 | 爱情岛亚洲品质自拍极速福利网站 | 国产成人精品免费 | 日韩久久一区二区三区 | 亚州| 中国白嫩丰满人妻videos | 亚洲日本免费 | 日本黄色一级 | 国产裸体永久免费无遮挡 | 亚洲xx在线 | 国产一卡二卡在线播放 | 亚洲品质自拍视频 | 91亚洲国产成人精品性色 | 亚洲自拍图片 | 青青操网站 | 少妇喷潮明星 | 日本久久精品 |