• 当前位置: 首 页 > 教育百科 > 学历/技能 > 正文

    java.lang.String 的 + 号操作到底做了什么?

    :2020年02月11日
    脚本之家

    在之前的面试经历中,对于String的考察还是挺频繁的,大致考察以下几个知识点:String 常量池new String()== 和 equals 的区别native 方法 String.intern()虽然面试中大体答对了,但是今天早上微信群里的...

    在之前的面试经历中,对于String的考察还是挺频繁的,大致考察以下几个知识点:

    • 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" 存放在哪个位置呢?

    解答分析(基于JDK1.8)

    下面也不靠猜,我们直接查看生成的字节码:

    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两个问题。

    • str1 是存放在常量池的

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

    下面我们看一下 + 操作做了什么事情,可以在Code中看到,该操作调用了 StringBuilder.append 方法

    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;

    那么到这里一切都答案都出来了

    • str2 是存放在堆中

    • equals 为 true

    • == 为 false

    所以说其实 str1 + " a nice day" 就相当于 new StringBuilder().append(str1).append(" a nice day");

    //这两种写法生成的字节码是一样的。

    //String str2 = str1 + " a nice day";

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

    StringBuffer的toString 方法如下:

    @Override

    public String toString() {

        // 所以说 str2 其实是一个 new String,是不在常量池里面的。

        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());

    }

    - END -

    [编辑:王振袢 &发表于江苏]
    [我要纠错]

    来源:本文内容搜集或转自各大网络平台,并已注明来源、出处,如果转载侵犯您的版权或非授权发布,请联系小编,我们会及时审核处理。
    声明:江苏教育黄页对文中观点保持中立,对所包含内容的准确性、可靠性或者完整性不提供任何明示或暗示的保证,不对文章观点负责,仅作分享之用,文章版权及插图属于原作者。

    关键词: 之前 面试 经历 对于 String
    有价值
    0
    无价值
    0
    猜您喜欢
    最热文章

    暂不支持手机端,请登录电脑端访问

    正在加载验证码......

    请先完成验证