<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          面試題:new String()創(chuàng)建幾個(gè)對(duì)象?

          共 2812字,需瀏覽 6分鐘

           ·

          2020-09-18 18:15

          常見(jiàn)面試問(wèn)題

          下面代碼中創(chuàng)建了幾個(gè)對(duì)象?

          new String("abc");

          答案眾說(shuō)紛紜,有說(shuō)創(chuàng)建了1個(gè)對(duì)象,也有說(shuō)創(chuàng)建了2個(gè)對(duì)象。答案對(duì),也不對(duì),關(guān)鍵是要學(xué)到問(wèn)題底層的原理。

          底層原理分析

          在上篇文章《面試題系列第1篇:說(shuō)說(shuō)==和equals的區(qū)別?你的回答可能是錯(cuò)誤的》中我們已經(jīng)提到,String的兩種初始化形式是有本質(zhì)區(qū)別的。

          String str1 = "abc";  // 在常量池中

          String str2 = new String("abc"); // 在堆上

          當(dāng)直接賦值時(shí),字符串“abc”會(huì)被存儲(chǔ)在常量池中,只有1份,此時(shí)的賦值操作等于是創(chuàng)建0個(gè)或1個(gè)對(duì)象。如果常量池中已經(jīng)存在了“abc”,那么不會(huì)再創(chuàng)建對(duì)象,直接將引用賦值給str1;如果常量池中沒(méi)有“abc”,那么創(chuàng)建一個(gè)對(duì)象,并將引用賦值給str1。

          那么,通過(guò)new String("abc");的形式又是如何呢?答案是1個(gè)或2個(gè)。

          當(dāng)JVM遇到上述代碼時(shí),會(huì)先檢索常量池中是否存在“abc”,如果不存在“abc”這個(gè)字符串,則會(huì)先在常量池中創(chuàng)建這個(gè)一個(gè)字符串。然后再執(zhí)行new操作,會(huì)在堆內(nèi)存中創(chuàng)建一個(gè)存儲(chǔ)“abc”的String對(duì)象,對(duì)象的引用賦值給str2。此過(guò)程創(chuàng)建了2個(gè)對(duì)象。

          當(dāng)然,如果檢索常量池時(shí)發(fā)現(xiàn)已經(jīng)存在了對(duì)應(yīng)的字符串,那么只會(huì)在堆內(nèi)創(chuàng)建一個(gè)新的String對(duì)象,此過(guò)程只創(chuàng)建了1個(gè)對(duì)象。

          在上述過(guò)程中檢查常量池是否有相同Unicode的字符串常量時(shí),使用的方法便是String中的intern()方法。

          public native String intern();

          下面通過(guò)一個(gè)簡(jiǎn)單的示意圖看一下String在內(nèi)存中的兩種存儲(chǔ)模式。

          上面的示意圖我們可以看到在堆內(nèi)創(chuàng)建的String對(duì)象的char value[]屬性指向了常量池中的char value[]。

          還是上面的示例,如果我們通過(guò)debug模式也能夠看到String的char value[]的引用地址。

          圖中兩個(gè)String對(duì)象的value值的引用均為{char[3]@1355},也就是說(shuō),雖然是兩個(gè)對(duì)象,但它們的value值均指向常量池中的同一個(gè)地址。當(dāng)然,大家還可以拿一個(gè)復(fù)雜對(duì)象(Person)的字符串屬性(name)相同時(shí)的debug結(jié)果進(jìn)行比對(duì),結(jié)果是一樣的。

          深入問(wèn)法

          如果面試官說(shuō)程序的代碼只有下面一行,那么會(huì)創(chuàng)建幾個(gè)對(duì)象?

          new String("abc");

          答案是2個(gè)?

          還真不一定。之所以單獨(dú)列出這個(gè)問(wèn)題是想提醒大家一點(diǎn):沒(méi)有直接的賦值操作(str="abc"),并不代表常量池中沒(méi)有“abc”這個(gè)字符串。也就是說(shuō)衡量創(chuàng)建幾個(gè)對(duì)象、常量池中是否有對(duì)應(yīng)的字符串,不僅僅由你是否創(chuàng)建決定,還要看程序啟動(dòng)時(shí)其他類(lèi)中是否包含該字符串。

          升級(jí)加碼

          以下實(shí)例我們暫且不考慮常量池中是否已經(jīng)存在對(duì)應(yīng)字符串的問(wèn)題,假設(shè)都不存在對(duì)應(yīng)的字符串。

          以下代碼會(huì)創(chuàng)建幾個(gè)對(duì)象:

          String str = "abc" + "def";

          上面的問(wèn)題涉及到字符串常量重載“+”的問(wèn)題,當(dāng)一個(gè)字符串由多個(gè)字符串常量拼接成一個(gè)字符串時(shí),它自己也肯定是字符串常量。字符串常量的“+”號(hào)連接Java虛擬機(jī)會(huì)在程序編譯期將其優(yōu)化為連接后的值。

          就上面的示例而言,在編譯時(shí)已經(jīng)被合并成“abcdef”字符串,因此,只會(huì)創(chuàng)建1個(gè)對(duì)象。并沒(méi)有創(chuàng)建臨時(shí)字符串對(duì)象abc和def,這樣減輕了垃圾收集器的壓力。

          我們通過(guò)javap查看class文件可以看到如下內(nèi)容。?很明顯,字節(jié)碼中只有拼接好的abcdef。

          針對(duì)上面的問(wèn)題,我們?cè)俅紊?jí)一下,下面的代碼會(huì)創(chuàng)建幾個(gè)對(duì)象?

          String str = "abc" + new String("def");

          創(chuàng)建了4個(gè),5個(gè),還是6個(gè)對(duì)象?

          4個(gè)對(duì)象的說(shuō)法:常量池中分別有“abc”和“def”,堆中對(duì)象new String("def")和“abcdef”。

          這種說(shuō)法對(duì)嗎?不完全對(duì),如果說(shuō)上述代碼創(chuàng)建了幾個(gè)字符串對(duì)象,那么可以說(shuō)是正確的。但上述的代碼Java虛擬機(jī)在編譯的時(shí)候同樣會(huì)優(yōu)化,會(huì)創(chuàng)建一個(gè)StringBuilder來(lái)進(jìn)行字符串的拼接,實(shí)際效果類(lèi)似:

          String s = new String("def");
          new StringBuilder().append("abc").append(s).toString();

          很顯然,多出了一個(gè)StringBuilder對(duì)象,那就應(yīng)該是5個(gè)對(duì)象。

          那么創(chuàng)建6個(gè)對(duì)象是怎么回事呢?有同學(xué)可能會(huì)想了,StringBuilder最后toString()之后的“abcdef”難道不在常量池存一份嗎?

          這個(gè)還真沒(méi)有存,我們來(lái)看一下這段代碼:

          @Test
          public void testString3() {
          String s1 = "abc";
          String s2 = new String("def");
          String s3 = s1 + s2;
          String s4 = "abcdef";
          System.out.println(s3==s4); // false
          }

          按照上面的分析,如果s1+s2的結(jié)果在常量池中存了一份,那么s3中的value引用應(yīng)該和s4中value的引用是一樣的才對(duì)。下面我們看一下debug的效果。

          很明顯,s3和s4的值相同,但value值的地址并不相同。即便是將s3和s4的位置調(diào)整一下,效果也一樣。s4很明確是存在于常量池中,那么s3對(duì)應(yīng)的值存儲(chǔ)在哪里呢?很顯然是在堆對(duì)象中。

          我們來(lái)看一下StringBuilder的toString()方法是如何將拼接的結(jié)果轉(zhuǎn)化為字符串的:

          @Override
          public
          String toString() {
          // Create a copy, don't share the array
          return new String(value, 0, count);
          }

          很顯然,在toString方法中又新創(chuàng)建了一個(gè)String對(duì)象,而該String對(duì)象傳遞數(shù)組的構(gòu)造方法來(lái)創(chuàng)建的:

          public String(char value[], int offset, int count)

          也就是說(shuō),String對(duì)象的value值直接指向了一個(gè)已經(jīng)存在的數(shù)組,而并沒(méi)有指向常量池中的字符串。

          因此,上面的準(zhǔn)確回答應(yīng)該是創(chuàng)建了4個(gè)字符串對(duì)象和1個(gè)StringBuilder對(duì)象。

          小結(jié)

          我們通過(guò)一行創(chuàng)建字符串的代碼逐步分析String對(duì)象的整個(gè)構(gòu)建及拼接過(guò)程,了解了底層實(shí)現(xiàn)原理。是不是很有意思?當(dāng)你掌握了這些底層基本知識(shí),即便面試題的形式如何變化,你必定能一眼識(shí)破真相。


          瀏覽 51
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  日韩一极片| 亚洲天堂在线免费观看 | 欧美精品卡一操逼 | 精品三级久久久 | 国产日韩精品无码去免费专区国产 |