面试官:String 拼接一定会走 StringBuilder?
关注公众号“面试专栏”,回复面试,获取优质面试资源。
本篇是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中,通过变量和字符串拼接,java是需要先到内存找变量对应的值,才能进行完成字符串拼接的工作,这种方式java编译器没法优化,只能走 StringBuilder
进行拼接字符串,然后调用toString方法,当然返回的结果和常量池中的111
这个字符串的内存地址是不一样的,因此结果为false。案例2中,直接在表达式里写值,java不用根据变量去内存里找对应的值,可以在编译的时候直接对这个表达式进行优化,优化后的表达式从 "111" + ""
直接变成了"111"
,两个String类型的变量都指向了常量池的111字符串,因此结果为true;
好了,今天就分享到这里,希望对你收割offer有所帮助,期待你的关注!
参考:c17oe.cn/1zlrw
精彩推荐