Java編程技術(shù)分享之字符串字面量

Java編程技術(shù)分享之字符串字面量

深圳達(dá)內(nèi)教育      2022-04-28 17:49:01     11

Java編程技術(shù)分享之字符串字面量,字符串是不可變的究竟什么使字符串字面量這么特殊?首先,記住重要的一點(diǎn)是字符串對(duì)象是不可變的。這就意味著一旦創(chuàng)建,一個(gè)字符

課程價(jià)格 請(qǐng)咨詢

上課時(shí)段: 授課校區(qū):

詳細(xì)介紹

字符串是不可變的

究竟什么使字符串字面量這么特殊?首先,記住重要的一點(diǎn)是字符串對(duì)象是不可變的。

這就意味著一旦創(chuàng)建,一個(gè)字符串對(duì)象就不能被改變(還是可以通過反射來改變)。

不可變?不能被更改?那怎么解釋這段代碼。

public?class?ImmutableStrings{????public?static?void?main(String[]?args)????{????????String?start?=?"Hello";????????String?end?=?start.concat("?World!");????????System.out.println(end);????}}//?OutputHello?World!

看這段代碼,字符串被改變了嗎,還是沒有?事實(shí)上,這段代碼中并沒有字符串對(duì)象被改變。

我們首先將“Hello”賦值給start變量,為了實(shí)現(xiàn)這步,需要在堆中創(chuàng)建一個(gè)對(duì)象,并把它的引用存儲(chǔ)在start中。接下來,我們?cè)谶@個(gè)對(duì)象上調(diào)用concat(String)方法。進(jìn)行到這里Java耍了一個(gè)小把戲,如果我們查看String的API說明,會(huì)發(fā)現(xiàn)其中對(duì)于concat(String)方法有如下的描述:

方法描述:將指定字符串連接在這個(gè)字符串的結(jié)尾。

如果長(zhǎng)度為0,則返回這個(gè)字符串對(duì)象。否則就創(chuàng)建一個(gè)新的字符串對(duì)象,表示這個(gè)字符串序列由原字符串對(duì)象和參數(shù)字符串二者所表示的字符串序列拼接而成。

你肯定看到了,當(dāng)你將兩個(gè)字符串做拼接操作時(shí),實(shí)際上并沒有改變?cè)瓕?duì)象,而是直接創(chuàng)建了一個(gè)包含原始對(duì)象的新的對(duì)象,并且將另一個(gè)字符串拼在了后面。

我們上面那段代碼就是這么執(zhí)行的,start變量所引用的字符串對(duì)象并沒有改變,如果在調(diào)用concat方法之后System.out.println(start);,會(huì)發(fā)現(xiàn)start仍然指向的是“Hello”。

這時(shí)候你可能想到了字符串中的“+”操作符,事實(shí)上字符串的“+”操作也是和concat做了同樣的事情(“+”操作實(shí)際上是new了一個(gè)StringBuilder對(duì)象,然后調(diào)用append方法)。

字符串的存儲(chǔ)——字符串常量池

你或許聽說過“字符串常量池”這個(gè)概念,究竟什么是字符串常量池?有人說是一個(gè)字符串對(duì)象容器。答案很接近了,但是不完全正確。

事實(shí)上他是一用來保存字符串對(duì)象引用的容器。

即使字符串是不可變的,它仍然和Java中的其他對(duì)象一樣。對(duì)象都是創(chuàng)建在堆中,字符串也不例外。

所以字符串常量池仍然依靠堆,他們存儲(chǔ)的只是堆中字符串的引用。

目前還沒有解釋這個(gè)池到底是什么,或者它為何存在。

好吧,因?yàn)樽址畬?duì)象是不可變的,所以復(fù)制多個(gè)引用來“共享”這個(gè)字符串是安全的。下面來看一個(gè)例子:

public?class?ImmutableStrings{????public?static?void?main(String[]?args)????{????????String?one?=?"someString";????????String?two?=?"someString";????????????????System.out.println(one.equals(two));????????System.out.println(one?==?two);????}}//?Outputtruetrue

在這個(gè)例子中,實(shí)在沒有必要為一個(gè)相同的字符串對(duì)象創(chuàng)建兩個(gè)實(shí)例。如果字符串像StringBuffer一樣是可變的,那么我們會(huì)被迫創(chuàng)建兩個(gè)對(duì)象(如果不這樣做的話,通過一個(gè)引用改變它的值,將會(huì)導(dǎo)致其他引用的值也同樣改變,從而可能發(fā)生錯(cuò)誤)。

但是,我們知道字符串對(duì)象是不能被改變的,我們可以安全地通過兩個(gè)引用one和two來使用一個(gè)字符串對(duì)象。

這個(gè)工作是通過字符串常量池完成的,下面來看一下它是如何完成的:

當(dāng)一個(gè).java文件被編譯成.class文件時(shí),和所有其他常量一樣,每個(gè)字符串字面量都通過一種特殊的方式被記錄下來。

當(dāng)一個(gè).class文件被加載時(shí)(注意加載發(fā)生在初始化之前),JVM在.class文件中尋找字符串字面量。

當(dāng)找到一個(gè)時(shí),JVM會(huì)檢查是否有相等的字符串在常量池中存放了堆中引用。

如果找不到,就會(huì)在堆中創(chuàng)建一個(gè)對(duì)象,然后將它的引用存放在池中的一個(gè)常量表中。

一旦一個(gè)字符串對(duì)象的引用在常量池中被創(chuàng)建,這個(gè)字符串在程序中的所有字面量引用都會(huì)被常量池中已經(jīng)存在的那個(gè)引用代替。

所以,在上面的例子中字符串常量池中只有一個(gè)引用,就是“someString”這個(gè)字符串對(duì)象的引用。

局部變量one和two都被賦予了同一個(gè)字符串對(duì)象的引用??梢酝ㄟ^程序的輸出來驗(yàn)證。

equals方法檢查的是兩個(gè)字符串對(duì)象是否包含相同的數(shù)據(jù)(“someString”),而“==”操作符作用在對(duì)象上比較的是引用是否相同,這意味著只有兩個(gè)引用指向的是同一個(gè)對(duì)象才會(huì)返回true。

所以例子中的兩個(gè)引用是相等的。從輸出可以看到,局部變量one和two不僅包含相同的數(shù)據(jù),而且還指向相同的對(duì)象。

無圖無真相,來看一下他們之間的關(guān)系:

注意,對(duì)于字符串字面量有一點(diǎn)比較特殊。通過“new”關(guān)鍵字構(gòu)建時(shí)一種不同的方式。

下面舉一個(gè)例子:

public?class?ImmutableStrings{????public?static?void?main(String[]?args)????{????????String?one?=?"someString";????????String?two?=?new?String("someString");????????????????System.out.println(one.equals(two));????????System.out.println(one?==?two);????}}//?Outputtruefalse

在這個(gè)例子中,可以看到由于關(guān)鍵字“new”,最后的結(jié)果有一點(diǎn)不同。

此例中,兩個(gè)字符串字面量仍然被放進(jìn)了常量池的常量表中,但是當(dāng)使用“new”時(shí),JVM就會(huì)在運(yùn)行時(shí)創(chuàng)建一個(gè)新對(duì)象,而不是使用常量表中的引用。

雖然例子中的兩個(gè)字符串引用所指向的對(duì)象包含相同的數(shù)據(jù)“someString”,但是這兩個(gè)對(duì)象并不相同。

這一點(diǎn)可以從輸出看出來,equals方法返回了true,而檢查引用是否相等的“==”返回false。

這表明兩個(gè)變量指向的是兩個(gè)不同的字符串對(duì)象。

如果你想看圖形化的表示,下面就是。要記住引用到常量池的字符串對(duì)象是在類加載的時(shí)候創(chuàng)建的,而另一個(gè)對(duì)象是在運(yùn)行時(shí),當(dāng)“new String”語句被執(zhí)行時(shí)。

如果你想得到兩個(gè)引用到相同對(duì)象的局部變量,你可以使用String類中的定義的intern()方法。

調(diào)用two.intern()后,會(huì)在字符串常量池中尋找是否有值相等的對(duì)象引用。

如果有的話,就會(huì)返回這個(gè)引用,然后你可以把它賦給局部變量。

如果這么做的化,局部變量one和two都是同一個(gè)對(duì)象的引用,并且在字符串常量池中也存有一個(gè)引用,就如同第一張圖那樣。這時(shí),在運(yùn)行時(shí)創(chuàng)建的第二個(gè)字符串對(duì)象將會(huì)被GC回收。

以上就是深圳達(dá)內(nèi)教育java培訓(xùn)機(jī)構(gòu)的小編針對(duì)“Java編程技術(shù)分享之字符串字面量”的內(nèi)容進(jìn)行的回答,希望對(duì)大家有所幫助,如有疑問,請(qǐng)?jiān)诰€咨詢,有專業(yè)老師隨時(shí)為你服務(wù)。

培訓(xùn)啦提醒您:交易時(shí)請(qǐng)核實(shí)對(duì)方資質(zhì),對(duì)于過大宣傳或承諾需謹(jǐn)慎!任何要求預(yù)付定金、匯款等方式均存在風(fēng)險(xiǎn),謹(jǐn)防上當(dāng)。