查看原文
其他

面试官:String 拼接一定会走 StringBuilder?

点击蓝字关注☞ 面试专栏 2021-08-29

关注公众号“面试专栏”,回复面试,获取优质面试资源。

面试专栏:第1-20期文章汇总

本篇是1000期面试系列文章的第64篇,持续更新中.....欢迎关注。

前言

String类是Java开发者使用最多的类,也因此,很多面试官很喜欢拿它来做文章,关于String涉及到的知识点还是蛮多的,今天我们来聊聊一个很奇怪的现象,但是搞清楚了,其实也见怪不怪了。

字符串拼接一定会走StringBuilder吗?哪种情况下会走会走StringBulider进行字符串拼接,而哪种情况编译器会对代码进行优化?

下面,我们就通过两个demo案例,已经字节码指令来分析一番。

问题

案例1

/**
 * 欢迎关注公众号:面试专栏
 *
 * @author admin
 * @date 2020/11/14 16:02
 */

public class StringDemo {
    public static void main(String[] args) throws Exception 
        String str = "abc";
        String temp = "abc";
        String str2 = temp + "";
        System.out.println(str == str2);
    }
}

上面这段代码的运行结果是false,言外之意就是str == str2的结果为false,也就是说str和str2不是一个对象。

我们继续看另外一个demo:

案例2

/**
 * 欢迎关注公众号:面试专栏
 *
 * @author admin
 * @date 2020/11/14 16:04
 */

public class StringDemo {
    public static void main(String[] args) throws Exception 
        String str = "abc";
        String temp = "abc";
        String str2 = "abc" + "";
        System.out.println(str == str2);
    }
}

上面这段代码输出的居然是true。也就是str和str2是同一个对象。

案例1和案例2看起来是差不多的,但是为什么有的相同有的不相同呢?下面我们继续探索。

探究问题

这时候,疑问就来了,为什么结果会不一致呢?利用在cmd窗口输入javap -c StringDemo.class >mszl.txt命令,查看其字节码(mszl是面试专栏的拼音首字母)。

javap命令介绍

javap 是JDK自带的反汇编器,可以查看java编译器为我们生成的字节码。通过它,我们可以对照源代码和字节码,从而了解很多编译器内部的工作。

语法:javap [ 命令选项 ] class. . .

javap 命令用于解析类文件。其输出取决于所用的选项。若没有使用选项, javap 将输出传递给它的类的 public 域及方法。javap 将其输出到标准输出设备上。

命令选项

  • -help 输出 javap 的帮助信息。
  • -l 输出行及局部变量表。
  • -b 确保与 JDK 1.1 javap 的向后兼容性。
  • -public 只显示 public 类及成员。
  • -protected 只显示 protected 和 public 类及成员。
  • -package 只显示包、protected 和 public 类及成员。这是缺省设置。
  • -private 显示所有类和成员。
  • -J[flag] 直接将 flag 传给运行时系统。
  • -s 输出内部类型签名。
  • -c 输出类中各方法的未解析的代码,即构成 Java 字节码的指令。
  • -verbose 输出堆栈大小、各方法的 locals 及 args 数,以及class文件的编译版本
  • -classpath[路径] 指定 javap用来查找类的路径。如果设置了该选项,则它将覆盖缺省值或 CLASSPATH 环境变量。目录用冒号分隔。
  • -bootclasspath[路径] 指定加载自举类所用的路径。缺省情况下,自举类是实现核心 Java 平台的类,位于 jrelib下面。
  • -extdirs[dirs]覆盖搜索安装方式扩展的位置。扩展的缺省位置是 jrelibext

案例1字节码如下:


可以看到在案例1中,java代码底层走了StringBuilder,进行字符串拼接,然后调用了StringBuilder的toString方法。

而案例2中,对class文件进行反编译,发现代码出现了一点变化,并没有走StringBuilder进行字符串拼接,请看下面案例2的字节码:


总结

  1. 案例1中,通过变量和字符串拼接,java是需要先到内存找变量对应的值,才能进行完成字符串拼接的工作,这种方式java编译器没法优化,只能走StringBuilder进行拼接字符串,然后调用toString方法,当然返回的结果和常量池中的111这个字符串的内存地址是不一样的,因此结果为false。
  2. 案例2中,直接在表达式里写值,java不用根据变量去内存里找对应的值,可以在编译的时候直接对这个表达式进行优化,优化后的表达式从 "111" + "" 直接变成了 "111" ,两个String类型的变量都指向了常量池的111字符串,因此结果为true;

好了,今天就分享到这里,希望对你收割offer有所帮助,期待你的关注!

参考:c17oe.cn/1zlrw

精彩推荐

面试官:说说一个 Java 对象有多大?

面试官:String、StringBuiler、StringBuffer,谁性能最高?

面试官:Java反射到底慢在哪?幸亏我看过这个知识点

面试官:项目中有用过多线程吗?

限时免费下载,程序员必备书籍

    : . Video Mini Program Like ,轻点两下取消赞 Wow ,轻点两下取消在看

    您可能也对以下帖子感兴趣

    文章有问题?点此查看未经处理的缓存