前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >String的+号操作到底做了什么事情

String的+号操作到底做了什么事情

作者头像
用户4283147
发布2022-10-08 12:45:37
3370
发布2022-10-08 12:45:37
举报
文章被收录于专栏:对线JAVA面试

前言

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

虽然面试中大体答对了,但是今天早上微信群里的一个问题我却答不上来,这个问题是这样的:

代码语言:javascript
复制
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)。

代码验证

代码语言:javascript
复制
 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);
    }

现在有以下几个问题,小伙伴们看看是否能答出来,即使答出来了,你知道为什么吗?

解答分析(基于JDK1.8)

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

代码语言:javascript
复制
localhost:test didi$ javap -verbose -p Main.class
Classfile /develop/project/string-test/out/production/classes/com/fanpan26/string/test/Main.class
  Last modified --; size  bytes
  MD5 checksum d1f1a23bfe85c2f88d2f767e8aac314
  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          #.#        // java/lang/Object."<init>":()V
   # = String             #            // what
   # = Class              #            // java/lang/StringBuilder
   # = Methodref          #.#         // java/lang/StringBuilder."<init>":()V
   # = Methodref          #.#         // java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
   # = String             #            //  a nice day
   # = Methodref          #.#         // java/lang/StringBuilder.toString:()Ljava/lang/String;
   # = Fieldref           #.#        // java/lang/System.out:Ljava/io/PrintStream;
   # = String             #            // what a nice day
  # = Methodref          #.#        // java/lang/String.equals:(Ljava/lang/Object;)Z
  # = Methodref          #.#        // java/io/PrintStream.println:(Z)V
  # = Class              #            // com/fanpan26/string/test/Main
  # = Class              #            // java/lang/Object
  # = Utf8               <init>
  # = Utf8               ()V
  # = Utf8               Code
  # = Utf8               LineNumberTable
  # = Utf8               LocalVariableTable
  # = Utf8               this
  # = Utf8               Lcom/fanpan26/string/test/Main;
  # = Utf8               main
  # = Utf8               ([Ljava/lang/String;)V
  # = Utf8               args
  # = Utf8               [Ljava/lang/String;
  # = Utf8               str1
  # = Utf8               Ljava/lang/String;
  # = Utf8               str2
  # = Utf8               StackMapTable
  # = Class              #            // "[Ljava/lang/String;"
  # = Class              #            // java/lang/String
  # = Class              #            // java/io/PrintStream
  # = Utf8               SourceFile
  # = Utf8               Main.java
  # = NameAndType        #:#        // "<init>":()V
  # = Utf8               what
  # = Utf8               java/lang/StringBuilder
  # = NameAndType        #:#        // append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
  # = Utf8                a nice day
  # = NameAndType        #:#        // toString:()Ljava/lang/String;
  # = Class              #            // java/lang/System
  # = NameAndType        #:#        // out:Ljava/io/PrintStream;
  # = Utf8               what a nice day
  # = Class              #            // java/lang/String
  # = NameAndType        #:#        // equals:(Ljava/lang/Object;)Z
  # = Class              #            // java/io/PrintStream
  # = NameAndType        #:#        // println:(Z)V
  # = Utf8               com/fanpan26/string/test/Main
  # = Utf8               java/lang/Object
  # = Utf8               java/lang/String
  # = Utf8               java/io/PrintStream
  # = Utf8               append
  # = Utf8               (Ljava/lang/String;)Ljava/lang/StringBuilder;
  # = Utf8               toString
  # = Utf8               ()Ljava/lang/String;
  # = Utf8               java/lang/System
  # = Utf8               out
  # = Utf8               Ljava/io/PrintStream;
  # = Utf8               equals
  # = Utf8               (Ljava/lang/Object;)Z
  # = Utf8               println
  # = Utf8               (Z)V
{
  public com.fanpan26.string.test.Main();
    descriptor: ()V
    flags: ACC_PUBLIC
    Code:
      stack=, locals=, args_size=
         : aload_0
         : invokespecial #                  // Method java/lang/Object."<init>":()V
         : return
      LineNumberTable:
        line : 
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
                          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=, locals=, args_size=
         : ldc           #                  // String what
         : astore_1
         : new           #                  // class java/lang/StringBuilder
         : dup
         : invokespecial #                  // Method java/lang/StringBuilder."<init>":()V
        : aload_1
        : invokevirtual #                  // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
        : ldc           #                  // String  a nice day
        : invokevirtual #                  // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
        : invokevirtual #                  // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
        : astore_2
        : getstatic     #                  // Field java/lang/System.out:Ljava/io/PrintStream;
        : ldc           #                  // String what a nice day
        : aload_2
        : invokevirtual #                 // Method java/lang/String.equals:(Ljava/lang/Object;)Z
        : invokevirtual #                 // Method java/io/PrintStream.println:(Z)V
        : getstatic     #                  // Field java/lang/System.out:Ljava/io/PrintStream;
        : ldc           #                  // String what a nice day
        : aload_2
        : if_acmpne     
        : iconst_1
        : goto          
        : iconst_0
        : invokevirtual #                 // Method java/io/PrintStream.println:(Z)V
        : return
      LineNumberTable:
        line : 
        line : 
        line : 
        line : 
        line : 
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
                         args   [Ljava/lang/String;
                         str1   Ljava/lang/String;
                        str2   Ljava/lang/String;
      StackMapTable: number_of_entries = 
        frame_type =  /* full_frame */
          offset_delta = 
          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 方法

代码语言:javascript
复制
: invokevirtual #5                  // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
: ldc           #6                  // String  a nice day
: invokevirtual #5                  // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
: 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");

代码语言:javascript
复制
//这两种写法生成的字节码是一样的。
//String str2 = str1 + " a nice day";
String str2 = new StringBuilder().append(str1).append(" a nice day").toString();

而StringBuilder 的toString 方法如下:

代码语言:javascript
复制
    @Override
    public String toString() {
        // 所以说 str2 其实是一个 new String,是不在常量池里面的。
        return new String(value, , count);
    }

总结

通过类的字节码可以查看底层具体用什么方式实现,所以说虽然看似一个简单的String问题,其实往深处挖掘还是考察了对生成的字节码的理解。还有,遇到一个问题,不能死记答案,有些人告诉你,+ 操作就是 new 对象,但是具体到底是不是或者为什么是有没有思考过呢?上文中如有错误,欢迎指出。

试一试

代码语言:javascript
复制
/**
     * 以下程序输出的结果是什么?
     * */
    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);
    }
代码语言:javascript
复制
/**
     * 以下程序输出的结果是什么?
     * */
    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);
    }
代码语言:javascript
复制
/**
     * 以下程序输出的结果是什么?
     * */
    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());
    }
本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2021-06-16,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 对线JAVA面试 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 前言
  • 代码验证
  • 解答分析(基于JDK1.8)
  • 总结
  • 试一试
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档