<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>

          Java字符串String那些事

          共 8340字,需瀏覽 17分鐘

           ·

          2021-07-04 15:10

          點(diǎn)擊藍(lán)色字關(guān)注我們!



          引言

          眾所周知在java里面除了8種基本數(shù)據(jù)類(lèi)型的話(huà),還有一種特殊的類(lèi)型String,這個(gè)類(lèi)型是我們每天搬磚都基本上要使用它。

          ?

          String 類(lèi)型可能是 Java 中應(yīng)用最頻繁的引用類(lèi)型,但它的性能問(wèn)題卻常常被忽略。高效的使用字符串,可以提升系統(tǒng)的整體性能。當(dāng)然,要做到高效使用字符串,需要深入了解其特性。

          ?

          String類(lèi)

          我們可以看下String類(lèi)的源碼:

          public final class String
              implements java.io.SerializableComparable<String>, CharSequence 
          {
              /** The value is used for character storage. */
              private final char value[];

          從源碼上我們是不是可以發(fā)現(xiàn)String類(lèi)是被final關(guān)鍵字所修飾的,String類(lèi)的數(shù)據(jù)是通過(guò)char[] 數(shù)組來(lái)存儲(chǔ)的。數(shù)組也是被final修飾的所以String 對(duì)象是不可被更改的。接下來(lái)我們?cè)倏纯碨tring的一些方法:像concat、replace、substring等都是返回了一個(gè)新的new String感興趣的可以去看看String的一些常見(jiàn)方法。當(dāng)我們執(zhí)行這些方法之后最原始的字符串是沒(méi)有改變的,都是返回新的字符串。

           public static void main(String[] args) {
                  String str = new String("java金融");
                  String str1 = str.substring(04);
                  String str2 = str.concat("公眾號(hào)");
                  String str3 = str.replace("java金融""關(guān)注:【java金融】");
                  // 還有其他的方法
                  System.out.println(str1);
                  System.out.println(str2);
                  System.out.println(str3);
                  System.out.println(str);
              }

          輸出結(jié)果

          java
          java金融公眾號(hào)
          關(guān)注:【java金融】
          java金融

          所以我們只要記住一點(diǎn):“String對(duì)象一旦被創(chuàng)建就是固定不變的了, 對(duì)String對(duì)象的任何改變都不影響到原對(duì)象,相關(guān)的任何change操作都會(huì)生成新的對(duì)象”。

          字符串常量池

          JVM中,為了減少字符串對(duì)象的重復(fù)創(chuàng)建,維護(hù)了一塊特殊的內(nèi)存空間,這塊內(nèi)存就被稱(chēng)為全局字符串常量池(string pool也有叫做string literal pool)。

          字符串常量池的位置

          字符串常量池所在的位置也是跟不同的jdk版本有關(guān)系的。

          • JDK6及之前字符串常量池存放在方法區(qū), 此時(shí)hotspot虛擬機(jī)對(duì)方法區(qū)的實(shí)現(xiàn)為永久代。
          • JDK7字符串常量池被從方法區(qū)拿到了堆中, 這里沒(méi)有提到運(yùn)行時(shí)常量池,也就是說(shuō)字符串常量池被單獨(dú)拿到堆,運(yùn)行時(shí)常量池剩下的東西還在方法區(qū), 也就是hotspot中的永久代。
          • JDK8 hotspot移除了永久代用元空間(Metaspace)取而代之, 這時(shí)候字符串常量池還在堆里只不過(guò)把方法區(qū)的實(shí)現(xiàn)從永久代變成了元空間(Metaspace) 。

          String# intern

          ?

          String::intern()是一個(gè)本地方法,它的作用是如果字符串常量池中已經(jīng)包含一個(gè)等于此String對(duì)象的字符串,則返回代表池中這個(gè)字符串的String對(duì)象的引用;否則,會(huì)將此String對(duì)象包含的字符串添加到常量池中,并且返回此String對(duì)象的引用。

          ?

          上述定義出自《深入理解Java虛擬機(jī):JVM高級(jí)特性與最佳實(shí)踐(第3版)》我們知道了這個(gè) String::intern()這個(gè)方法的作用下面來(lái)看幾道并沒(méi)有什么用的題目看看你是否都能夠回答對(duì)?

                  String str2 = new String("java") + new String("金融"); // 1
                   str2.intern(); // 2
                   String str1 = "java金融"// 3
                   System.out.println(str2 == str1);

          這個(gè)代碼在JDK6中輸出結(jié)果是false,在jdk7輸出是true。為何會(huì)因?yàn)椴煌?code style="font-size: 14px;overflow-wrap: break-word;margin-right: 2px;margin-left: 2px;font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(53, 148, 247);background: rgba(59, 170, 250, 0.1);padding-right: 2px;padding-left: 2px;border-radius: 2px;height: 21px;line-height: 22px;">jdk版本輸出結(jié)果不一樣,因?yàn)椴煌姹咀址A砍氐奈恢冒l(fā)生了變化。下面來(lái)分析下為何會(huì)產(chǎn)生這種差異。字符串雖然不屬于基本數(shù)據(jù)類(lèi)型但是它也可以想基本類(lèi)型一樣,直接通過(guò)字面量來(lái)賦值,同時(shí)也是可以通過(guò)new 來(lái)生成字符串對(duì)象。通過(guò)字面量賦值的方式和new 的方式 生成字符串還是有區(qū)別的。

          • 字面量賦值:通過(guò)字面量賦值(使用雙引號(hào)聲明出來(lái)的String)會(huì)先去常量池中查找是否已經(jīng)有相同的字符串,如果已經(jīng)存在棧中的引用直接指向該字符串,如果不存在就在常量中生成一個(gè)字符串再將棧中的引用指向該字符串。
          • new 的方式創(chuàng)建:而通過(guò)new的方式創(chuàng)建字符串時(shí),就直接在堆中生成一個(gè)字符串的對(duì)象棧中的引用指向該對(duì)象。對(duì)于堆中的字符串對(duì)象,可以通過(guò) intern() 方法來(lái)將字符串添加的常量池中,并返回指向該常量的引用。jdk6 結(jié)果是false,是因?yàn)槌A砍厥窃谟谰么腜erm區(qū)和java堆是兩個(gè)區(qū)域。所以?xún)蓚€(gè)區(qū)域的對(duì)象地址比較是不同的。JDK7結(jié)果是true, 這個(gè)原因主要是從JDK 7及以后,HotSpot 將常量池從永久代移到了堆,正因?yàn)槿绱耍?code style="font-size: 14px;overflow-wrap: break-word;margin-right: 2px;margin-left: 2px;font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(53, 148, 247);background: rgba(59, 170, 250, 0.1);padding-right: 2px;padding-left: 2px;border-radius: 2px;height: 21px;line-height: 22px;">JDK7 及以后的intern方法在實(shí)現(xiàn)上發(fā)生了比較大的改變,JDK7及以后,intern方法還是會(huì)先去查詢(xún)常量池中是否有已經(jīng)存在,如果存在,則返回常量池中的引用,這一點(diǎn)與之前沒(méi)有區(qū)別,區(qū)別在于如果在常量池找不到對(duì)應(yīng)的字符串則不會(huì)再將字符串拷貝到常量池,而只是在常量池中生成一個(gè)對(duì)原字符串的引用。所以為什么返回true 是因?yàn)閳?zhí)行完標(biāo)號(hào)為1的時(shí)候常量池中沒(méi)有"「java金融」"對(duì)象的,接下來(lái)標(biāo)號(hào)為2的時(shí)候 會(huì)在常量池生成一個(gè)“「java金融」”的對(duì)象會(huì)直接存一個(gè)對(duì)堆中“「java金融」”的引用,標(biāo)號(hào)為3:進(jìn)行字面量賦值的時(shí)候常量池已經(jīng)存在了所以直接返回該引用。所以都是指向堆中的字符串返回true。「如果把3行代碼放到第一行上面結(jié)果又不一樣了,感興趣的可以動(dòng)手試一試并且分析下原因哦?!?/strong>

          string 常見(jiàn)性能優(yōu)化

          使用+號(hào)拼接字符串

          字符串拼接是我們平時(shí)在代碼中使用最頻繁的了。

          • +號(hào)拼接靜態(tài)字符串
             String str = "關(guān)注"+"公眾號(hào):"+"java金融";

          我們可以通過(guò)反編譯查看下上述代碼:

           public static void main(java.lang.String[]);
              descriptor: ([Ljava/lang/String;)V
              flags: ACC_PUBLIC, ACC_STATIC
              Code:
                stack=1, locals=2, args_size=1
                   0: ldc           #2                  // String 關(guān)注公眾號(hào):java金融
                   2: astore_1
                   3return
                LineNumberTable:
                  line 110
                  line 123
          }

          我們可以發(fā)現(xiàn)編譯器直接幫我們優(yōu)化了,直接生成了一個(gè)字符串“關(guān)注公眾號(hào):java金融” 并沒(méi)有生成中間變量的String實(shí)例。如果我們上述代碼稍微變化下

             public static void main(String[] args) {
                  String str ="關(guān)注";
                  String str1 = str + "公眾號(hào):java金融";
              }

           stack=2, locals=3, args_size=1
                   0: ldc           #2                  // String 關(guān)注
                   2: astore_1
                   3new           #3                  // class java/lang/StringBuilder
                   6: dup
                   7: invokespecial #4                  // Method java/lang/StringBuilder."<init>":()V
                  10: aload_1
                  11: invokevirtual #5                  // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
                  14: ldc           #6                  // String 公眾號(hào):java金融
                  16: invokevirtual #5                  // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
                  19: invokevirtual #7                  // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
                  22: astore_2
                  23return
                LineNumberTable:
                  line 110
                  line 123
                  line 1323

          從反編譯代碼中我們會(huì)發(fā)現(xiàn)生成了StringBuilder對(duì)象來(lái)進(jìn)行追加。

          • 所以String + 拼接變量的時(shí)候底層是通過(guò)StringBuilder來(lái)實(shí)現(xiàn)的,我們循環(huán)操作拼接字符串的時(shí)候也應(yīng)當(dāng)使用StringBuilder替代+,否則的話(huà)每一次循環(huán)都會(huì)創(chuàng)建 一個(gè)StringBuilder 對(duì)象。
          • 對(duì)于靜態(tài)字符串的拼接操作,Java在編譯時(shí)會(huì)進(jìn)行徹底的優(yōu)化,會(huì)把多個(gè)拼接字符串在編譯時(shí)合成一個(gè)單獨(dú)的長(zhǎng)字符串。

          常見(jiàn)字符串經(jīng)典面試題

          關(guān)于字符串最常見(jiàn)的面試題,面試寶典常見(jiàn)的題目。「String s = new String("xyz")」 創(chuàng)建了多少個(gè)實(shí)例?一般的回答都會(huì)是2個(gè),(一個(gè)是“xyz”,一個(gè)是指向“xyz”的引用對(duì)象s) 答案并沒(méi)有那么簡(jiǎn)單哦,可以看看大佬的回答還是非常精彩的。連接地址https://www.iteye.com/blog/rednaxelafx-774673(文末第一個(gè)參考地址)

          結(jié)束

          • 由于自己才疏學(xué)淺,難免會(huì)有紕漏,假如你發(fā)現(xiàn)了錯(cuò)誤的地方,還望留言給我指出來(lái),我會(huì)對(duì)其加以修正。
          • 如果你覺(jué)得文章還不錯(cuò),你的轉(zhuǎn)發(fā)、分享、贊賞、點(diǎn)贊、留言就是對(duì)我最大的鼓勵(lì)。
          • 感謝您的閱讀,十分歡迎并感謝您的關(guān)注。

          • — 【 THE END 】—
            本公眾號(hào)全部博文已整理成一個(gè)目錄,請(qǐng)?jiān)诠娞?hào)里回復(fù)「m」獲?。?/span>

            最近面試BAT,整理一份面試資料Java面試BATJ通關(guān)手冊(cè),覆蓋了Java核心技術(shù)、JVM、Java并發(fā)、SSM、微服務(wù)、數(shù)據(jù)庫(kù)、數(shù)據(jù)結(jié)構(gòu)等等。

            獲取方式:點(diǎn)“在看”,關(guān)注公眾號(hào)并回復(fù) PDF 領(lǐng)取,更多內(nèi)容陸續(xù)奉上。

            文章有幫助的話(huà),在看,轉(zhuǎn)發(fā)吧。

            謝謝支持喲 (*^__^*)

          瀏覽 67
          點(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>
                  亚洲专区欧美专区 | 蜜桃视频18 | 网站一区二区三区 | 俺来俺来也www色官网 | 91人妻人人澡人人爽精品 |