本篇文章為大家展示了如何理解Java 虛擬機(jī)中的String 類和常量池,內(nèi)容簡(jiǎn)明扼要并且容易理解,絕對(duì)能使你眼前一亮,通過這篇文章的詳細(xì)介紹希望你能有所收獲。
成都創(chuàng)新互聯(lián)公司專注于納雍企業(yè)網(wǎng)站建設(shè),成都響應(yīng)式網(wǎng)站建設(shè)公司,購(gòu)物商城網(wǎng)站建設(shè)。納雍網(wǎng)站建設(shè)公司,為納雍等地區(qū)提供建站服務(wù)。全流程按需策劃,專業(yè)設(shè)計(jì),全程項(xiàng)目跟蹤,成都創(chuàng)新互聯(lián)公司專業(yè)和態(tài)度為您提供的服務(wù)一、String 對(duì)象的兩種創(chuàng)建方式
String str1 = "abcd"; String str2 = new String("abcd"); System.out.println(str1==str2); //false
這兩種不同的創(chuàng)建方法是有差別的:
第一種方式是在常量池中獲取對(duì)象(“abcd” 屬于字符串字面量,因此編譯時(shí)期會(huì)在常量池中創(chuàng)建一個(gè)字符串對(duì)象);
第二種方式一共會(huì)創(chuàng)建兩個(gè)字符串對(duì)象(前提是 String Pool 中還沒有 “abcd” 字符串對(duì)象)。
“abcd” 屬于字符串字面量,因此編譯時(shí)期會(huì)在常量池中創(chuàng)建一個(gè)字符串對(duì)象,該字符串對(duì)象指向這個(gè) “abcd” 字符串字面量;
使用 new 的方式會(huì)在堆中創(chuàng)建一個(gè)字符串對(duì)象。
str1 指向常量池中的 “abcd”,而 str2 指向堆中的字符串對(duì)象。
intern() 方法設(shè)計(jì)的初衷,就是重用 String 對(duì)象,以節(jié)省內(nèi)存消耗。
JDK6:當(dāng)調(diào)用intern方法的時(shí)候,如果字符串常量池先前已創(chuàng)建出該字符串對(duì)象,則返回常量池中的該字符串的引用。否則,將此字符串對(duì)象添加到字符串常量池中,并且返回該字符串對(duì)象的引用。
JDK6+:當(dāng)調(diào)用intern方法的時(shí)候,如果字符串常量池先前已創(chuàng)建出該字符串對(duì)象,則返回常量池中的該字符串的引用。否則,如果該字符串對(duì)象已存在與Java堆中,則將堆中對(duì)此對(duì)象的引用添加到字符串常量池中,并且返回該引用;如果堆中不存在,則在常量池中創(chuàng)建該字符串并返回其引用。
在 JVM 運(yùn)行時(shí)數(shù)據(jù)區(qū)中的方法區(qū)有一個(gè)常量池,但是發(fā)現(xiàn)在 JDK 1.6 以后常量池被放置在了堆空間,因此常量池位置的不同影響到了 String 的 intern() 方法的表現(xiàn)。
String s = new String("1"); s.intern(); String s2 = "1"; System.out.println(s == s2); String s3 = new String("1") + new String("1"); s3.intern(); String s4 = "11"; System.out.println(s3 == s4);
JDK 1.6 及以下
上述代碼輸出結(jié)果:
false false
解釋:
在 JDK 1.6 中所有的輸出結(jié)果都是 false,因?yàn)?JDK 1.6 以及以前版本中,常量池是放在 PermGen 區(qū)(屬于方法區(qū))中的,而方法區(qū)和堆區(qū)是完全分開的。
使用引號(hào)聲明的字符串會(huì)直接在字符串常量池中生成的,而 new 出來的 String 對(duì)象是放在堆空間中的。所以兩者的內(nèi)存地址肯定是不相同的,即使調(diào)用了 intern() 方法也是不影響的。
intern() 方法在 JDK 1.6 中的作用:比如 String s = new String(“1”);,再調(diào)用 s.intern(),此時(shí)返回值還是字符串”1”,表面上看起來好像這個(gè)方法沒什么用處。但實(shí)際上,在 JDK1.6 中:檢查字符串常量池里是否存在 “1” 這么一個(gè)字符串,如果存在,就返回池里的字符串;如果不存在,該方法會(huì)把 “1” 添加到字符串常量池中,然后再返回它的引用。
JDK 1.6 及以上
上述代碼輸出結(jié)果:
false true
解釋:String s= new String("1")
生成了字符串常量池中的 “1” 和堆空間中的字符串對(duì)象。
s.intern() s
對(duì)象去字符串常量池中尋找后,發(fā)現(xiàn) “1” 已存在于常量池中。
String s2 = "1"
生成 s2 的引用指向常量池中的 “1” 對(duì)象。
顯然,s 和 s2 的引用地址是不同的。
String s3 = new String("1") + new String("1")
在字符串常量池中生成 “1”,并在堆空間中生成 s3 引用指向的對(duì)象(內(nèi)容為 “11”)。 注意此時(shí)常量池中是沒有 “11” 對(duì)象。
s3.intern()
將 s3 中的 “11” 字符串放入字符串常量池中。 JDK 1.6 的做法是直接在常量池中生成一個(gè) “11” 的對(duì)象。但在 JDK 1.7 中,常量池中不需要再存儲(chǔ)一份對(duì)象了,可以直接存儲(chǔ)堆中的引用。這份引用直接指向 s3 引用的對(duì)象,也就是說 s3.intern() == s3 會(huì)返回 true。
String s4 = "11"
, 這一行代碼會(huì)直接去常量池中創(chuàng)建,但是發(fā)現(xiàn)已經(jīng)有這個(gè)對(duì)象了,此時(shí) s4 就是指向 s3 引用對(duì)象的一個(gè)引用。因此 s3 == s4 返回了true。
String str1 = "str"; String str2 = "ing"; String str3 = "str" + "ing";//常量池中的對(duì)象 String str4 = str1 + str2; //TODO:在堆上創(chuàng)建的新的對(duì)象 String str5 = "string";//常量池中的對(duì)象 System.out.println(str3 == str4);//false System.out.println(str3 == str5);//true System.out.println(str4 == str5);//false
注意:盡量避免多個(gè)字符串拼接,因?yàn)檫@樣會(huì)重新創(chuàng)建對(duì)象。 如果需要改變字符串的話,可以使用 StringBuilder 或者 StringBuffer。
面試題:String s1 = new String(“abc”);問創(chuàng)建了幾個(gè)對(duì)象?
創(chuàng)建2個(gè)字符串對(duì)象(前提是 String Pool 中還沒有 “abcd” 字符串對(duì)象)。
“abc” 屬于字符串字面量,因此編譯時(shí)期會(huì)在常量池中創(chuàng)建一個(gè)字符串對(duì)象,指向這個(gè) “abcd” 字符串字面量;
使用 new 的方式會(huì)在堆中創(chuàng)建一個(gè)字符串對(duì)象。
(字符串常量”abc”在編譯期就已經(jīng)確定放入常量池,而 Java 堆上的”abc”是在運(yùn)行期初始化階段才確定)。
String s1 = new String("abc");// 堆內(nèi)存的地址值 String s2 = "abc"; System.out.println(s1 == s2);// 輸出false //因?yàn)橐粋€(gè)是堆內(nèi)存,一個(gè)是常量池的內(nèi)存,故兩者是不同的。 System.out.println(s1.equals(s2));// 輸出true
上述內(nèi)容就是如何理解Java 虛擬機(jī)中的String 類和常量池,你們學(xué)到知識(shí)或技能了嗎?如果還想學(xué)到更多技能或者豐富自己的知識(shí)儲(chǔ)備,歡迎關(guān)注創(chuàng)新互聯(lián)-成都網(wǎng)站建設(shè)公司行業(yè)資訊頻道。