1.
string str1 = "abc";
string str2 = new string("abc");
這兩種建立string物件的方法有什麼不同?
2.
string s = "a" + "b" + "c" + "d";
這裡面一共建立了多少物件?
這兩道題昨天給筆者搞得是一臉懵逼,後來一聽這是一道很經典的面試題,就趕快查閱各種資料,現在已經解決了。
在解決這兩個題之前,我們先來明確幾個知識點,相信把這幾個知識點弄完之後再回頭看這兩個題,就很簡單了:
1.引用在棧記憶體中儲存,物件在堆記憶體中儲存。
2.string物件不可變。
3.string建立物件的形式:
4.字串常量池的意義:
5.intern()方法使用。
我們首先來開第乙個:
引用在棧記憶體中儲存,物件在堆記憶體中儲存。
這個是我粗略畫的一張圖,這張圖可能不是很準確,但是我只想表達乙個意思,我們的棧記憶體,存放的是我們物件的引用和我們基本資料型別的值。而堆記憶體中存放的是我們的物件。就是這麼簡單。
這裡我們用一下大佬的圖:
如果用**表示上面的圖,那就是:
string s = "abcd";
s = s + "el";
如果沒有上面這張圖,大家可能會覺得我們只是給s物件後面加了乙個el,這不還是原來的s嗎?不,我們說的string物件不可變就是這個意思,當我們給s再加上el時候,新的字串abcdel被存放到了乙個新的記憶體中。已經不是原來的記憶體了。所以說string物件不可變,如果變了,那就已經變成了乙個新的物件。
那說到這裡大家可能會返回去看我寫的第二個問題:按你的說法這不就7個物件嗎?abcd各佔乙個,每次+到一起都要重新生成物件,一共生成了7個物件。話是沒錯,但是我想說這道題說多解,等我們講完了下面這兩個知識點,這道題就完全解開了。
正如我們上面看到的,string有兩種建立物件的形式:
1.字面量形式:字串常量池,又稱為字串在字面量池。大家不要他想象的多麼高深額,其實說白了他就是一塊記憶體,它裡面存放的是我們的字串的引用。2.標準的new形式:string str = "asd";
string str = new string("asd");
大家可能疑問這個東西有什麼意義:假如我們要建立乙個字串,"a" + "b" + "c" + ....+ 我們+了一萬次,那麼按照上面的說法,我們是不是為了建立乙個很長的字串,建立了很多的物件。這樣不但有的字串被重複的建立了,而且占用了很多不必要的記憶體,代價有點大。
我們的jvm為了減少字串的重複建立,維護了乙個特殊的記憶體:就是這個字串常量池。
在這個字串常量池中,存放著我們字串物件的引用。下面我們根據字串建立物件的兩種形式來說明一下常量池是如何存放string物件引用的:
1.通過字面量建立string物件:
我們舉個例子,string str = "abc";我們通過字面量形式建立乙個值為"abc"的物件,首先我們判斷常量池中是否存在乙個引用,這個引用的值也是"abc",如果有這個值,那麼我們就從常量池中拿到這個引用,返回給str。
如果沒有,那麼我們就建立這個物件,然後把str這個引用值放到常量池中。
在這裡我們要明確幾個點了:
1首先常量池存放的不是字串物件,而是字串物件的引用。2.無論常量池中是否存在這個物件的引用,最後結果都會存放有這個引用。3.該方法可能不會建立新的物件。2.通過new建立string物件。
這裡其實只有一句話,無論常量池中是否存在相同值的引用,至少建立乙個物件。因為不管別的,new這個語法就一定會建立乙個新的物件,然後我們要看構造方法中的字串,如果常量池中存在與該字串相同值的引用,那麼就不會建立新的物件,反之會建立物件之後,在常量池中存放這個引用。
這樣看起來我們的常量池的意義就很明確了,減少重複建立string物件,從而減少不必要的記憶體消耗。
如果要說他的弊端的話,應該就是犧牲了cpu的計算時間來換取空間吧,因為查詢是否有相同內容的引用是cpu耗時計算,但是與前者占用記憶體相比,這個還是算不了什麼的吧(筆者自己觀點,沒有官方支援哈哈)。
最後我們來看一下這個intern()方法,這個方法其實理解為檢視常量池中是否存在對應內容的引用。如果有他會返回常量池中的引用,如果沒有則會將當前string的引用放入常量池。
我們舉幾個例子對比下就好:
public static void main(string args)
他的結果是true。很簡單,我們首先通過字面量形式建立了乙個「gfzy」物件,此時常量池中沒有相同內容引用,所以常量池存放str1的引用。
然後str2為str1的.intern()方法,現在常量池中存在「gfzy」這個內容的引用,所以我們返回這個引用,也就是str1,所以str1和str2指向同乙個引用。
public static void main(string args)
這個結果為false,首先我們看到str1是new了乙個物件,所以他肯定是在堆記憶體中一塊新的記憶體,而構造方法中的「gfzy」,首先在常量池中會拿到他的引用,然後返回這個引用。
str2直接拿到了常量池中的引用,所以乙個是堆記憶體新建立的,乙個是原來常量池中的引用,兩者指向不是乙個記憶體,所以是false。
而如果是這樣呢:
public static void main(string args)
這個結果為true。在原來的基礎上只是新增了乙個inter方法,在new string("gfzy");時候,現在常量池中已經有了這個引用。而現在intern,我們會拿到常量池中的引用,所以str1為常量池中的引用,str2和上面一樣,所以返回true。
現在這幾個問題都解決完了,我們再返回頭看之前的兩個問題,第乙個問題就很簡單了,而我們剛才也說過了。
字串操作 str
len str 獲取字串長度 str.find 查詢,從頭到尾找到第乙個符合的就停止 str.rfind 查詢,從尾到頭找到第乙個符合的就停止 沒有找到字串的時候返回 1 str.index 類似find str.rindex 類似rfind 沒有找到字串的時候報錯 str.startswith 以...
python中的str 字串 的介紹及常見操作
作用 儲存當前字元,字母,或者數字及其他中文 定義 可以使用 或者 是等價的關係 如果想保留文字格式 需要使用 三引號 或者 單純的顯示雙引號 需要 或者 三個單個 引 號 字串 下標 索引 獲取對應的字元,順序不能變換 從左到右 1,2,3,從右到左 1,2,3.計算最大索引 len 變數名 le...
python中字串(str)的操作
s hello 字串的重疊 s hello 2 字串的拼接 s hello world print s 統計字元個數 print len s 提取單個字元,通過下表提取 從開頭提取,下表從0開始 print s 0 從結尾提取,下表從 1開始 print s 1 切片 s 開始 結束 步進 s ab...