String"+"拼接底層實現原理

String:常量,不可變,不適合用來字符串拼接,每次都是新創(chuàng)建的對象,消耗較大。
StringBuffer:適合用來作字符串拼接
StringBuilder:JDK1.5引入,適合用來作字符串拼接,與StringBuffer區(qū)別是他不是線程安全的

接下來進入正題String”+”拼接底層實現原理

public class StringDemo01 {

    public static void main(String[] args) {
        String a = "abc";
        String b = "def";
        System.out.println("abcdef" == a+b);
    }
}

通過javap命令分析java匯編指令可以得知底層使用了StringBuilder實現

javap -v StringDemo.class
Classfile StringDemo01.class
  Last modified 2020-6-6; size 730 bytes
  MD5 checksum 8847314e26430be9703f9490a6d8ecf3
  Compiled from "StringDemo01.java"
public class string.StringDemo01
  minor version: 0
  major version: 52
  flags: ACC_PUBLIC, ACC_SUPER
Constant pool:
   #1 = Methodref          #12.#25        // java/lang/Object."<init>":()V
   #2 = String             #26            // abc
   #3 = String             #27            // def
   #4 = Fieldref           #28.#29        // java/lang/System.out:Ljava/io/PrintStream;
   #5 = String             #30            // abcdef
   #6 = Class              #31            // java/lang/StringBuilder
   #7 = Methodref          #6.#25         // java/lang/StringBuilder."<init>":()V
   #8 = Methodref          #6.#32         // java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
   #9 = Methodref          #6.#33         // java/lang/StringBuilder.toString:()Ljava/lang/String;
  #10 = Methodref          #34.#35        // java/io/PrintStream.println:(Z)V
  #11 = Class              #36            // string/StringDemo01
  #12 = Class              #37            // java/lang/Object
  #13 = Utf8               <init>
  #14 = Utf8               ()V
  #15 = Utf8               Code
  #16 = Utf8               LineNumberTable
  #17 = Utf8               main
  #18 = Utf8               ([Ljava/lang/String;)V
  #19 = Utf8               StackMapTable
  #20 = Class              #38            // "[Ljava/lang/String;"
  #21 = Class              #39            // java/lang/String
  #22 = Class              #40            // java/io/PrintStream
  #23 = Utf8               SourceFile
  #24 = Utf8               StringDemo01.java
  #25 = NameAndType        #13:#14        // "<init>":()V
  #26 = Utf8               abc
  #27 = Utf8               def
  #28 = Class              #41            // java/lang/System
  #29 = NameAndType        #42:#43        // out:Ljava/io/PrintStream;
  #30 = Utf8               abcdef
  #31 = Utf8               java/lang/StringBuilder
  #32 = NameAndType        #44:#45        // append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
  #33 = NameAndType        #46:#47        // toString:()Ljava/lang/String;
  #34 = Class              #40            // java/io/PrintStream
  #35 = NameAndType        #48:#49        // println:(Z)V
  #36 = Utf8               string/StringDemo01
  #37 = Utf8               java/lang/Object
  #38 = Utf8               [Ljava/lang/String;
  #39 = Utf8               java/lang/String
  #40 = Utf8               java/io/PrintStream
  #41 = Utf8               java/lang/System
  #42 = Utf8               out
  #43 = Utf8               Ljava/io/PrintStream;
  #44 = Utf8               append
  #45 = Utf8               (Ljava/lang/String;)Ljava/lang/StringBuilder;
  #46 = Utf8               toString
  #47 = Utf8               ()Ljava/lang/String;
  #48 = Utf8               println
  #49 = Utf8               (Z)V
{
  public string.StringDemo01();
    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 11: 0

  public static void main(java.lang.String[]);
    descriptor: ([Ljava/lang/String;)V
    flags: ACC_PUBLIC, ACC_STATIC
    Code:
      stack=4, locals=3, args_size=1
         0: ldc           #2                  // String abc
         2: astore_1
         3: ldc           #3                  // String def
         5: astore_2
         6: getstatic     #4                  // Field java/lang/System.out:Ljava/io/PrintStream;
         9: ldc           #5                  // String abcdef
        11: new           #6                  // class java/lang/StringBuilder
        14: dup
        15: invokespecial #7                  // Method java/lang/StringBuilder."<init>":()V
        18: aload_1
        19: invokevirtual #8                  // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
        22: aload_2
        23: invokevirtual #8                  // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
        26: invokevirtual #9                  // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
        29: if_acmpne     36
        32: iconst_1
        33: goto          37
        36: iconst_0
        37: invokevirtual #10                 // Method java/io/PrintStream.println:(Z)V
        40: return
      LineNumberTable:
        line 14: 0
        line 15: 3
        line 17: 6
        line 21: 40
      StackMapTable: number_of_entries = 2
        frame_type = 255 /* full_frame */
          offset_delta = 36
          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: "StringDemo01.java"

String拼接,有字符串變量參與時,中間會產生StringBuilder對象(JDK1.5之前產生StringBuffer)

字符串拼接原理:運行時, 兩個字符串str1, str2的拼接首先會調用 String.valueOf(obj),這個Obj為str1,而String.valueOf(Obj)中的實現是return obj == null ? “null” : obj.toString(), 然后產生StringBuilder, 調用的StringBuilder(str1)構造方法, 把StringBuilder初始化,長度為str1.length()+16,并且調用append(str1)! 接下來調用StringBuilder.append(str2), 把第二個字符串拼接進去, 然后調用StringBuilder.toString返回結果!

StringBuilder(str) 底層調用

   /**
     * Constructs a string builder initialized to the contents of the
     * specified string. The initial capacity of the string builder is
     * {@code 16} plus the length of the string argument.
     *
     * @param   str   the initial contents of the buffer.
     */
    public StringBuilder(String str) {
        super(str.length() + 16);
        append(str);
    }

StringBuilder.toString 底層調用

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

Javap

介紹其中code區(qū)(匯編指令)、局部變量表和代碼行偏移映射三個部分。

javap是jdk自帶的反解析工具。它的作用就是根據class字節(jié)碼文件,反解析出當前類對應的code區(qū)(匯編指令)、本地變量表、異常表和代碼行偏移量映射表、常量池等等信息。
當然這些信息中,有些信息(如本地變量表、指令和代碼行偏移量映射表、常量池中方法的參數名稱等等)需要在使用javac編譯成class文件時,指定參數才能輸出,比如,你直接javac xx.java,就不會在生成對應的局部變量表等信息,如果你使用javac -g xx.java就可以生成所有相關信息了。
通過反編譯生成的匯編代碼,我們可以深入的了解java代碼的工作機制。比如我們可以查看i++;這行代碼實際運行時是先獲取變量i的值,然后將這個值加1,最后再將加1后的值賦值給變量i。
通過局部變量表,我們可以查看局部變量的作用域范圍、所在槽位等信息,甚至可以看到槽位復用等信息。

Links

https://blog.csdn.net/qq_36771269/article/details/80818200

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
【社區(qū)內容提示】社區(qū)部分內容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

友情鏈接更多精彩內容