Java常量池詳解
java中有幾種不同的常量池,以下的內(nèi)容是對(duì)java中幾種常量池的介紹,其中最常見(jiàn)的就是字符串常量池。
(1)class常量池
在Java中,Java類(lèi)被編譯后就會(huì)形成一份class文件;class文件中除了包含類(lèi)的版本、字段、方法、接口等描述信息外,還有一項(xiàng)信息就是常量池,用于存放編譯器生成的各種字面量和符號(hào)引用,每個(gè)class文件都有一個(gè)class常量池。
其中字面量包括:1.文本字符串 2.八種基本類(lèi)型的值 3.被聲明為final的常量等;
符號(hào)引用包括:1.類(lèi)和方法的全限定名 2.字段的名稱(chēng)和描述符 3.方法的名稱(chēng)和描述符。
(2)運(yùn)行時(shí)常量池
運(yùn)行時(shí)常量池存在于內(nèi)存中,也就是class常量池被加載到內(nèi)存之后的版本,是方法區(qū)的一部分(JDK1.8 運(yùn)行時(shí)常量池在元空間,元空間也是方法區(qū)的一種實(shí)現(xiàn))。不同之處是:它的字面量可以動(dòng)態(tài)的添加(String類(lèi)的intern()),符號(hào)引用可以被解析為直接引用。
JVM在執(zhí)行某個(gè)類(lèi)的時(shí)候,必須經(jīng)過(guò)加載、連接、初始化,而連接又包括驗(yàn)證、準(zhǔn)備、解析三個(gè)階段。而當(dāng)類(lèi)加載到內(nèi)存中后,jvm就會(huì)將class常量池中的內(nèi)容存放到運(yùn)行時(shí)常量池中,這里所說(shuō)的常量包括:基本類(lèi)型包裝類(lèi)(包裝類(lèi)不管理浮點(diǎn)型,整形只會(huì)管理-128到127)和字符串類(lèi)型(即通過(guò)String.intern()方法可以強(qiáng)制將String放入常量池),運(yùn)行時(shí)常量池是每個(gè)類(lèi)私有的。在解析階段,會(huì)把符號(hào)引用替換為直接引用。
(3)基本類(lèi)型包裝類(lèi)常量池
Java 基本類(lèi)型的包裝類(lèi)的大部分都實(shí)現(xiàn)了常量池技術(shù)。Byte,Short,Integer,Long這 4 種包裝類(lèi)默認(rèn)創(chuàng)建了數(shù)值 [-128,127] 的相應(yīng)類(lèi)型的緩存數(shù)據(jù),Character創(chuàng)建了數(shù)值在[0,127]范圍的緩存數(shù)據(jù),Boolean直接返回True或False,如果超出對(duì)應(yīng)范圍就會(huì)去創(chuàng)建新的對(duì)象。兩種浮點(diǎn)數(shù)類(lèi)型的包裝類(lèi)Float,Double并沒(méi)有實(shí)現(xiàn)常量池技術(shù)。
Integer 緩存源碼:
/**
*此方法將始終緩存-128 到 127(包括端點(diǎn))范圍內(nèi)的值,并可以緩存此范圍之外的其他值。
*/
public static Integer valueOf(int i) {
if (i >= IntegerCache.low && i <= IntegerCache.high)
return IntegerCache.cache[i + (-IntegerCache.low)];
return new Integer(i);
}
private static class IntegerCache {
static final int low = -128;
static final int high;
static final Integer cache[];
}
舉個(gè)栗子:
Integer i1 = 40;
Integer i2 = 40;
Integer i3 = 0;
Integer i4 = new Integer(40);
Integer i5 = new Integer(40);
Integer i6 = new Integer(0);
System.out.println("i1=i2 " + (i1 == i2));
System.out.println("i1=i2+i3 " + (i1 == i2 + i3));
System.out.println("i1=i4 " + (i1 == i4));
System.out.println("i4=i5 " + (i4 == i5));
System.out.println("i4=i5+i6 " + (i4 == i5 + i6));
System.out.println("40=i5+i6 " + (40 == i5 + i6));
結(jié)果:
i1=i2 true
i1=i2+i3 true
i1=i4 false
i4=i5 false
i4=i5+i6 true
40=i5+i6 true
解釋:1-4語(yǔ)句結(jié)果應(yīng)該很顯然,因?yàn)镮nteger i1=40 這一行代碼會(huì)發(fā)生裝箱,也就是說(shuō)這行代碼等價(jià)于 Integer i1=Integer.valueOf(40),Integer.valueOf()方法基于減少對(duì)象創(chuàng)建次數(shù)和節(jié)省內(nèi)存的考慮,緩存了[-128,127]之間的數(shù)字,如果在此數(shù)字范圍內(nèi)直接返回緩存中的對(duì)象。在此之外,直接new出來(lái),顯然40在常量池的緩存[-128,127]范圍內(nèi);因此,i1 直接使用的是常量池中的對(duì)象。而Integer i1 = new Integer(40) 會(huì)直接創(chuàng)建新的對(duì)象;語(yǔ)句 i4 == i5 + i6,因?yàn)?這個(gè)操作符不適用于 Integer 對(duì)象,首先 i5 和 i6 進(jìn)行自動(dòng)拆箱操作,進(jìn)行數(shù)值相加,即 i4 == 40。然后 Integer 對(duì)象無(wú)法與數(shù)值進(jìn)行直接比較,所以 i4 自動(dòng)拆箱轉(zhuǎn)為 int 值 40,最終這條語(yǔ)句轉(zhuǎn)為 40 == 40 進(jìn)行數(shù)值比較,所以結(jié)果為true。第六條語(yǔ)句同理。
額外說(shuō)明:所有整型包裝類(lèi)對(duì)象之間值的比較,全部使用 equals 方法比較。
對(duì)于Integer var = ?在-128至127之間的賦值,Integer對(duì)象是在 IntegerCache.cache產(chǎn)生,會(huì)復(fù)用已有對(duì)象,這個(gè)區(qū)間內(nèi)的Integer值可以直接使用==進(jìn)行判斷,但是這個(gè)區(qū)間之外的所有數(shù)據(jù),都會(huì)在堆上產(chǎn)生,并不會(huì)復(fù)用已有對(duì)象,推薦使用equals方法進(jìn)行判斷。
(4)字符串常量池
在JDK1.6及之前版本,字符串常量池存放在方法區(qū)中的,在JDK1.7版本以后,字符串常量池被移到了堆中了。
HotSpot VM里,記錄interned string的一個(gè)全局表叫做StringTable,它本質(zhì)上就是個(gè)HashSet<String>;這個(gè)StringTable在每個(gè)HotSpot VM的實(shí)例只有一份,被所有的類(lèi)共享。
注意:它只存儲(chǔ)對(duì)java.lang.String實(shí)例的引用,而不存儲(chǔ)String對(duì)象的內(nèi)容
字符串常量池和上面的基本類(lèi)型包裝類(lèi)常量池有些不同,字符串常量池中沒(méi)有事先緩存一些數(shù)據(jù),而是如果要?jiǎng)?chuàng)建的字符串在常量池內(nèi)存在就返回對(duì)象的引用,如果不存在就創(chuàng)建一個(gè)放在常量池中;
在Java中,有兩種創(chuàng)建字符串對(duì)象的方法,一種是字面量直接創(chuàng)建,另一種是new一個(gè)String對(duì)象,這兩種方法創(chuàng)建字符串對(duì)象的過(guò)程會(huì)不一樣;
(1)String str = "abc";
(2)String str = new String("abc");
如果是第一種方式創(chuàng)建對(duì)象,因?yàn)槭亲置媪恐苯觿?chuàng)建,所以在編譯的時(shí)候是確定的,如果該字符串不在常量池中會(huì)將該字符串放入常量池中并返回字符串對(duì)象的引用,如果在常量池中直接返回字符串對(duì)象的引用,如果是第二種方式創(chuàng)建對(duì)象,因?yàn)橐獎(jiǎng)?chuàng)建String類(lèi)型的對(duì)象,String對(duì)象是在運(yùn)行時(shí)才加載到內(nèi)存的堆中的,屬于運(yùn)行時(shí)創(chuàng)建,所以要先在堆中創(chuàng)建一個(gè)String對(duì)象,再去常量池中尋找是否有相同的字符串,如果有就返回堆中Sring對(duì)象的引用,如果沒(méi)有則在將該字符串加入常量池中。
舉個(gè)栗子:
比較下列兩種創(chuàng)建字符串的方法:
String str1 = new String("abc");
String str2 = "abc";
答案:第一種是用new()來(lái)新建對(duì)象的,它會(huì)在存放于堆中。每調(diào)用一次就會(huì)創(chuàng)建一個(gè)新的對(duì)象。 運(yùn)行時(shí)期創(chuàng)建 。
第二種是先在棧中創(chuàng)建一個(gè)對(duì)String類(lèi)的對(duì)象引用變量str2,然后通過(guò)符號(hào)引用去字符串常量池里找有沒(méi)有”abc”,如果沒(méi)有,則將”abc”存放進(jìn)字符串常量池,并令str2指向”abc”,如果已經(jīng)有”abc” 則直接令str2指向“abc”?!癮bc”存于常量池在 編譯期間完成 。
String s = new String("abc")
這條語(yǔ)句創(chuàng)建了幾個(gè)對(duì)象?
答案:共2個(gè)。第一個(gè)對(duì)象是”abc”字符串存儲(chǔ)在常量池中,第二個(gè)對(duì)象在Java Heap中的 String 對(duì)象。這里不要混淆了s是放在棧里面的指向了Heap堆中的String對(duì)象。
String s1 = new String("s1") ;
String s1 = new String("s1") ;
上面一共創(chuàng)建了幾個(gè)對(duì)象?
答案:3個(gè) ,編譯期常量池中創(chuàng)建1個(gè),運(yùn)行期堆中創(chuàng)建2個(gè).(用new創(chuàng)建的每new一次就在堆上創(chuàng)建一個(gè)對(duì)象,用引號(hào)創(chuàng)建的如果在常量池中已有就直接指向,不用創(chuàng)建)
總結(jié)
本篇文章就到這里了,希望能夠給你帶來(lái)幫助,也希望您能夠多多關(guān)注腳本之家的更多內(nèi)容!
相關(guān)文章
如何使用Spring MVC的消息轉(zhuǎn)換器設(shè)置日期格式
這篇文章主要介紹了如何使用Spring MVC的消息轉(zhuǎn)換器設(shè)置日期格式,本文通過(guò)示例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2023-07-07
SpringBoot整合MyBatis四種常用的分頁(yè)方式(詳細(xì)總結(jié))
這篇文章詳細(xì)給大家總結(jié)了SpringBoot整合MyBatis四種常用的分頁(yè)方式,文中通過(guò)代碼示例為大家介紹的非常詳細(xì),需要的朋友可以參考下2023-07-07
關(guān)于Java使用Http輕量級(jí)請(qǐng)求庫(kù)Unirest的方法
這篇文章主要介紹了關(guān)于Java使用Http輕量級(jí)請(qǐng)求庫(kù)Unirest的方法,Unirest 是一個(gè)輕量級(jí)的 HTTP 請(qǐng)求庫(kù),可發(fā)起 GET, POST, PUT, PATCH, DELETE, HEAD, OPTIONS 請(qǐng)求,支持 Node、Ruby、Java、PHP、Python、Objective-C、.NET 等多種語(yǔ)言,需要的朋友可以參考下2023-08-08
mybatis?log4j2打印sql+日志實(shí)例代碼
在學(xué)習(xí)mybatis的時(shí)候,如果用log4j2來(lái)協(xié)助查看調(diào)試信息,則會(huì)大大提高學(xué)習(xí)的效率,加快debug速度,下面這篇文章主要給大家介紹了關(guān)于mybatis?log4j2打印sql+日志的相關(guān)資料,需要的朋友可以參考下2022-08-08
簡(jiǎn)單理解Java的垃圾回收機(jī)制與finalize方法的作用
這篇文章主要介紹了簡(jiǎn)單理解Java的垃圾回收機(jī)制與finalize方法的作用,著重講解了Java的GC銷(xiāo)毀對(duì)象的過(guò)程,需要的朋友可以參考下2015-11-11
Java實(shí)現(xiàn)五子棋的基礎(chǔ)方法
這篇文章主要為大家詳細(xì)介紹了Java實(shí)現(xiàn)五子棋的基礎(chǔ)方法,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2020-09-09
spring?cloud中Feign導(dǎo)入jar失敗的問(wèn)題及解決方案
這篇文章主要介紹了spring?cloud中Feign導(dǎo)入jar失敗的問(wèn)題及解決方案,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-03-03
Spring?Boot+Aop記錄用戶操作日志實(shí)戰(zhàn)記錄
在Spring框架中使用AOP配合自定義注解可以方便的實(shí)現(xiàn)用戶操作的監(jiān)控,下面這篇文章主要給大家介紹了關(guān)于Spring?Boot+Aop記錄用戶操作日志實(shí)戰(zhàn)的相關(guān)資料,文中通過(guò)實(shí)例代碼介紹的非常詳細(xì),需要的朋友可以參考下2023-04-04

