java.lang.String 的 + 號操做到底作了什麼事情?

前言

 在以前的面試經歷中,對於String的考察仍是挺頻繁的,大體考察如下幾個知識點:java

  • String 常量池
  • new String()
  • == 和 equals 的區別
  • native 方法 String.intern()
    雖然面試中大致答對了,可是今天早上微信羣裏的一個問題我卻答不上來,這個問題是這樣的:
String str3 = "what";
    String str4 = str3 + " a nice day";
    //運行時, + 至關於 new,因此堆中會有 "what a nice day"對象,常量池中會有"what"," a nice day"兩個對象,而不會有 "what a nice day"對象。
    //這句話大佬們看看對不對啊,我怎麼感受不對啊
    //常量池不會有"what a nice day" 對象嗎?

看完這個問題,說實話我也是有點懵的,我只是知道 "what a nice day"不會在常量池,可是不知道具體的緣由,後來羣裏的同窗說 + 號是調用了 StringBuffer 的append 方法。我去證明了,發現確實調用了 append 方法,可是當時沒有 調用toString()方法,我很疑惑。(最後通過證明,是StringBuilder的append 方法,不是StringBuffer)。面試

代碼驗證

public static void main(String[] args) {
        //#1
        String str1 = "what";
        //#2
        String str2 = str1 + " a nice day";
        //#3
        System.out.println("what a nice day".equals(str2));
        //#4
        System.out.println("what a nice day" == str2);
    }

如今有如下幾個問題,小夥伴們看看是否能答出來,即便答出來了,你知道爲何嗎?微信

  • #1 str1 存放位置?
  • #2 str2 存放位置?
  • #3 結果是 true 仍是 false?
  • #4 結果是 true 仍是 false?
  • #5 "what a nice day" 存放在哪一個位置呢?

解答分析

下面也不靠猜,咱們直接查看生成的字節碼:app

localhost:test didi$ javap -verbose -p Main.class
Classfile /develop/project/string-test/out/production/classes/com/fanpan26/string/test/Main.class
  Last modified 2019-11-29; size 972 bytes
  MD5 checksum 1d1f1a23bfe85c2f88d2f767e8aac314
  Compiled from "Main.java"
public class com.fanpan26.string.test.Main
  minor version: 0
  major version: 52
  flags: ACC_PUBLIC, ACC_SUPER
Constant pool:
   #1 = Methodref          #13.#34        // java/lang/Object."<init>":()V
   #2 = String             #35            // what
   #3 = Class              #36            // java/lang/StringBuilder
   #4 = Methodref          #3.#34         // java/lang/StringBuilder."<init>":()V
   #5 = Methodref          #3.#37         // java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
   #6 = String             #38            //  a nice day
   #7 = Methodref          #3.#39         // java/lang/StringBuilder.toString:()Ljava/lang/String;
   #8 = Fieldref           #40.#41        // java/lang/System.out:Ljava/io/PrintStream;
   #9 = String             #42            // what a nice day
  #10 = Methodref          #43.#44        // java/lang/String.equals:(Ljava/lang/Object;)Z
  #11 = Methodref          #45.#46        // java/io/PrintStream.println:(Z)V
  #12 = Class              #47            // com/fanpan26/string/test/Main
  #13 = Class              #48            // java/lang/Object
  #14 = Utf8               <init>
  #15 = Utf8               ()V
  #16 = Utf8               Code
  #17 = Utf8               LineNumberTable
  #18 = Utf8               LocalVariableTable
  #19 = Utf8               this
  #20 = Utf8               Lcom/fanpan26/string/test/Main;
  #21 = Utf8               main
  #22 = Utf8               ([Ljava/lang/String;)V
  #23 = Utf8               args
  #24 = Utf8               [Ljava/lang/String;
  #25 = Utf8               str1
  #26 = Utf8               Ljava/lang/String;
  #27 = Utf8               str2
  #28 = Utf8               StackMapTable
  #29 = Class              #24            // "[Ljava/lang/String;"
  #30 = Class              #49            // java/lang/String
  #31 = Class              #50            // java/io/PrintStream
  #32 = Utf8               SourceFile
  #33 = Utf8               Main.java
  #34 = NameAndType        #14:#15        // "<init>":()V
  #35 = Utf8               what
  #36 = Utf8               java/lang/StringBuilder
  #37 = NameAndType        #51:#52        // append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
  #38 = Utf8                a nice day
  #39 = NameAndType        #53:#54        // toString:()Ljava/lang/String;
  #40 = Class              #55            // java/lang/System
  #41 = NameAndType        #56:#57        // out:Ljava/io/PrintStream;
  #42 = Utf8               what a nice day
  #43 = Class              #49            // java/lang/String
  #44 = NameAndType        #58:#59        // equals:(Ljava/lang/Object;)Z
  #45 = Class              #50            // java/io/PrintStream
  #46 = NameAndType        #60:#61        // println:(Z)V
  #47 = Utf8               com/fanpan26/string/test/Main
  #48 = Utf8               java/lang/Object
  #49 = Utf8               java/lang/String
  #50 = Utf8               java/io/PrintStream
  #51 = Utf8               append
  #52 = Utf8               (Ljava/lang/String;)Ljava/lang/StringBuilder;
  #53 = Utf8               toString
  #54 = Utf8               ()Ljava/lang/String;
  #55 = Utf8               java/lang/System
  #56 = Utf8               out
  #57 = Utf8               Ljava/io/PrintStream;
  #58 = Utf8               equals
  #59 = Utf8               (Ljava/lang/Object;)Z
  #60 = Utf8               println
  #61 = Utf8               (Z)V
{
  public com.fanpan26.string.test.Main();
    descriptor: ()V
    flags: ACC_PUBLIC
    Code:
      stack=1, locals=1, args_size=1
         0: aload_0
         1: invokespecial #1                  // Method java/lang/Object."<init>":()V
         4: return
      LineNumberTable:
        line 6: 0
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0       5     0  this   Lcom/fanpan26/string/test/Main;

  public static void main(java.lang.String[]);
    descriptor: ([Ljava/lang/String;)V
    flags: ACC_PUBLIC, ACC_STATIC
    Code:
      stack=3, locals=3, args_size=1
         0: ldc           #2                  // String what
         2: astore_1
         3: new           #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  a nice day
        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
        23: getstatic     #8                  // Field java/lang/System.out:Ljava/io/PrintStream;
        26: ldc           #9                  // String what a nice day
        28: aload_2
        29: invokevirtual #10                 // Method java/lang/String.equals:(Ljava/lang/Object;)Z
        32: invokevirtual #11                 // Method java/io/PrintStream.println:(Z)V
        35: getstatic     #8                  // Field java/lang/System.out:Ljava/io/PrintStream;
        38: ldc           #9                  // String what a nice day
        40: aload_2
        41: if_acmpne     48
        44: iconst_1
        45: goto          49
        48: iconst_0
        49: invokevirtual #11                 // Method java/io/PrintStream.println:(Z)V
        52: return
      LineNumberTable:
        line 9: 0
        line 11: 3
        line 13: 23
        line 15: 35
        line 16: 52
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0      53     0  args   [Ljava/lang/String;
            3      50     1  str1   Ljava/lang/String;
           23      30     2  str2   Ljava/lang/String;
      StackMapTable: number_of_entries = 2
        frame_type = 255 /* full_frame */
          offset_delta = 48
          locals = [ class "[Ljava/lang/String;", class java/lang/String, class java/lang/String ]
          stack = [ class java/io/PrintStream ]
        frame_type = 255 /* full_frame */
          offset_delta = 0
          locals = [ class "[Ljava/lang/String;", class java/lang/String, class java/lang/String ]
          stack = [ class java/io/PrintStream, int ]
}
SourceFile: "Main.java"

Constant pool: 中的信息能夠看到,#2 #6 #9 能夠解答上文中的#1,#5兩個問題。ide

  • str1 是存放在常量池的
  • "what a nice day" (非str2)也是存放在常量池的.

下面咱們看一下 + 操做作了什麼事情,能夠在Code中看到,該操做調用了 StringBuilder.append 方法ui

11: invokevirtual #5                  // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
        14: ldc           #6                  // String  a nice day
        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;

那麼到這裏一切都答案都出來了this

  • str2 是存放在堆中。
  • equals 爲 true
  • == 爲 false

因此說其實 str1 + " a nice day" 就至關於 new StringBuilder().append(str1).append(" a nice day");code

@Override
    public String toString() {
        // 因此說 str2 實際上是一個 new String,是不在常量池裏面的。
        return new String(value, 0, count);
    }

總結

經過類的字節碼能夠查看底層具體用什麼方式實現,因此說雖然看似一個簡單的String問題,其實往深處挖掘仍是考察了對生成的字節碼的理解。還有,遇到一個問題,不能死記答案,有些人告訴你,+ 操做就是 new 對象,可是具體究竟是不是或者爲何是有沒有思考過呢?上文中若有錯誤,歡迎指出。對象

相關文章
相關標籤/搜索