java.lang.String 的 + 號操作,這個謎終於要解開了!

2021-03-02 Java大後端

作者:丶Pz  
來源:https://www.cnblogs.com/panzi/p/11956782.html

在之前的面試經歷中,對於String的考察還是挺頻繁的,大致考察以下幾個知識點:

雖然面試中大體答對了,但是今天早上微信群裡的一個問題我卻答不上來,這個問題是這樣的:

String str3 = "what";
String str4 = str3 + " a nice day";



看完這個問題,說實話我也是有點懵的,我只是知道 "what a nice day"不會在常量池,但是不知道具體的原因,後來群裡的同學說 + 號是調用了 StringBuffer 的append 方法。我去證實了,發現確實調用了 append 方法,但是當時沒有 調用toString()方法,我很疑惑。(最後經過證實,是StringBuilder的append 方法,不是StringBuffer)。



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

現在有以下幾個問題,小夥伴們看看是否能答出來,即使答出來了,你知道為什麼嗎?"what a nice day" 存放在哪個位置呢?

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 
          offset_delta = 48
          locals = [ class "[Ljava/lang/String;", class java/lang/String, class java/lang/String ]
          stack = [ class java/io/PrintStream ]
        frame_type = 255 
          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兩個問題。"what a nice day" (非str2)也是存放在常量池的下面我們看一下 + 操作做了什麼事情,可以在Code中看到,該操作調用了 StringBuilder.append 方法

11: invokevirtual #5                  
14: ldc           #6                  
16: invokevirtual #5                  
19: invokevirtual #7                  

所以說其實 str1 + " a nice day" 就相當於 new StringBuilder().append(str1).append(" a nice day");



String str2 = new StringBuilder().append(str1).append(" a nice day").toString();

而StringBuilder 的toString 方法如下:

@Override
public String toString() {
    
    return new String(value, 0, count);
}


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




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


public static void main(String[] args) {
    String str1 = "what a nice day";
    String str2 = new String("what a nice day");
    System.out.println(str1.equals(str2));
    System.out.println(str1 == str2);
}


public static void main(String[] args) {
    String str1 = "what";
    String str2 = str1.concat(" a nice day");
    System.out.println("what a nice day".equals(str2));
    System.out.println("what a nice day" == str2);
    System.out.println("what a nice day"==str2.intern());
}

(完)

最近熱門:

——長按關注Java大後端——

相關焦點

  • java.lang.String 的 + 號操作到底做了什麼?
    看完這個問題,說實話我也是有點懵的,我只是知道 "what a nice day"不會在常量池,但是不知道具體的原因,後來群裡的同學說 + 號是調用了 StringBuffer 的append 方法。我去證實了,發現確實調用了 append 方法,但是當時沒有 調用toString()方法,我很疑惑。
  • java程式設計師面試遇到string題如何不涼?
    別涼,今天小編就為大家準備了面試中常遇到的string題,讓你輕鬆愉快拿offer~首先跟大家說說如何創建string~創建字符串較簡單的方式如下String str = "Runoob";在代碼中遇到字符串常量時,這裡的值是 "Runoob"",編譯器會使用該值創建一個 String 對象。
  • 你真的了解java的lambda嗎?- java lambda用法與源碼分析
    *     * @see     java.lang.Thread#run()     */    public abstract void run();}分析->這個箭頭是lambda表達式的關鍵操作符
  • Java 中的 String 有沒有長度限制?
    根據Integer類的定義,java.lang.Integer#MAX_VALUE的最大值是2^31 - 1;那麼,我們是不是就可以認為String能支持的最大長度就是這個值了呢?其實並不是,這個值只是在運行期,我們構造String的時候可以支持的一個最大長度,而實際上,在運行期,定義字符串的時候也是有長度限制的。
  • java.lang.nullpointerexception 怎麼解決及出現原因?-CSDN
    相信做過java開發的都遇到過java.lang.nullpointerexception異常,即是空指針異常。解決方法:定位到報空指針異常的這一行代碼,找到有"." 即有點的地方,任何有"."點的地方,都有可能報空指針異常。
  • String是java的基礎變量嗎?
    什麼意思看不起我?咦,有點模糊?此時面試官的問題宛如一句sql,我那可憐的單核大腦超頻的執行著這個單線程任務,同時不斷的列印出日誌:「嗯...」,"應該...","那個...",「我記得...」,"額..."
  • 學習一下Java 11新增的String實用方法
    () {String output = "La ".repeat(2) + "Land";is(output).equals("La La Land");}``` 如果對空字符串或長度為0的字符串做操作
  • Java反射機制深入詳解
    反射是java語言的一個特性,它允程序在運行時(注意不是編譯的時候)來進行自我檢查並且對內部的成員進行操作。例如它允許一個java的類獲取他所有的成員變量和方法並且顯示出來。Java 的這一能力在實際應用中也許用得不是很多,但是在其它的程序設計語言中根本就不存在這一特性。例如,Pascal、C 或者 C++ 中就沒有辦法在程序中獲得函數定義相關的信息。
  • Java 14 發布了,終於可以扔掉Lombok了?
    我們先嘗試對他進行編譯,記得使用--enable-preview參數,因為records功能目前在JDK 14中還是一個預覽(preview)功能。javac --enable-preview --release 14 Person.javaNote: Person.java uses preview language features.Note: Recompile with -Xlint:preview for details.
  • 深入淺出Rhino:Java與JS互操作
    3 2011 05 09  js>  註:第一行為js解釋器的版本號,),在效果上等價於Java聲明import java.io.*; 不同的是,Java會隱式import java.lang.Rhino如何與Java對象交互  與Java類似,Rhino使用new操作符創建對象。
  • 淺談Java中字符串的初始化及字符串操作類
    說白了就是: 字符串常量池提供了字符串的復用功能, 除非我們要顯式創建新的字符串對象, 否則對同一個字符串虛擬機只會維護一份拷貝。 配合反編譯代碼驗證字符串初始化操作.相信看到這裡, 再見到有關的面試題, 你已經無所畏懼了, 因為你已經懂得了背後原理。
  • Java編程中常見的異常
    這個異常大家肯定都經常遇到,異常的解釋是"程序遇上了空指針",簡單地說就是調用了未經初始化的對象或者是不存在的對象,這個錯誤經常出現在創建圖片,調用數組這些操作中,比如圖片未經初始化,或者圖片創建時的路徑錯誤等等。對數組操作中出現空指針,很多情況下是一些剛開始學習編程的朋友常犯的錯誤,即把數組的初始化和數組元素的初始化混淆起來了。
  • Java之String重點解析
    那麼為了服務字符串不可變性的設計,則衍生出非常多的相關問題:為什麼要保持其不可變?底層如何存儲字符串?如何進行字符串操作才擁有更好的性能?等等。此外,字符編碼的相關知識也是非常重要;畢竟,現在使用emoij是再正常不過的事情了。
  • java中utils方法 - CSDN
    commons-lang 是老版本,已經很久沒有維護了。commons-lang3 是一直在維護的版本,推薦直接使用這個版本。注意:如果你系統已經有 commons-lang,注意如果直接替換成 commons-lang3,將會編譯錯誤。commons-lang3 中相關類與 commons-lang 一樣,但是包名不一樣。
  • 你還以為使用 StringBuffer 就萬事大吉了?
    (3).append(4)你知道最後結果可能有多少種情況嗎如果你要insert, 你需要知道自己是要insert到哪一個位置,比如在第一個出現的媳婦前插入一句我愛你三個字,那你寫代碼的話就是兩行代碼int index = stringBuffer.indexOf(「媳婦」)
  • java基礎編程題之String字符串練習
    5.字符串操作: 1)從字符串「java程序訓練營20100228」中提取開班日期 2)將「CSDN JAVA」字符串中的「JAVA」替換為「J2EE」。 3)取出「java程序訓練營20100228」第8個字符。 4)清除「java程序訓練營20100228」中所有的0。
  • 泛型Java程式設計師必備基礎
    System.out.println(x.getClass().getName()); } public static void main(String[] args) { GenericMethods gm = new GenericMethods(); gm.f("字符串"); gm.f(666); }}複製代碼運行結果:java.lang.Stringjava.lang.Integer
  • 面試官:Java 反射是什麼?我回答不上來!
    反射是java語言的一個特性,它允程序在運行時(注意不是編譯的時候)來進行自我檢查並且對內部的成員進行操作。例如它允許一個java的類獲取他所有的成員變量和方法並且顯示出來。Java 的這一能力在實際應用中也許用得不是很多,但是在其它的程序設計語言中根本就不存在這一特性。例如,Pascal、C 或者 C++ 中就沒有辦法在程序中獲得函數定義相關的信息。
  • 一文讀懂 Java 字符串相關知識點和常見面試題
    作者:黃小斜來源:微信公眾號【黃小斜】目錄string基礎Java String 類創建字符串StringDemo.java 文件代碼:String基本用法創建String對象的常用方法String中常用的方法